内存管理
stdlib库中有几个内存管理相关的函数
序号 | 函数和描述 |
---|---|
1 | void *calloc(int num, int size); 在内存中动态地分配 num 个长度为size 个字节 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 |
2 | void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
3 | void *malloc(int size); 在堆区分配一块size个字节的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
4 | void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize,newsize的单位还是字节 |
总之三个alloc分配的都是字节,calloc分配num个size字节的空间,malloc分配size个字节的空间,realloc重新分配size个字节的空间,三个都返回分配的空间的基址,然后用一个指针存这个基址,free()用来释放掉这个指针.
如:
#include<stdio.h>
#include<stdlib.h>
int main(){
int *a=calloc(10,4);
realloc(a,80);
for(int i=0;i<20;i++){
*(a+i)=2*i;
}
for(int i=0;i<20;i++){
printf("%d ",*(a+i));
}
free(a);
}
分配10个4字节空间,一个4字节的空间可以存一个int或long,之后重新分配80个字节,也就是之前空间的两倍
两次for循环存数,读数,最后释放掉指针
问题1:
重新分配内存会不会把之前的存储释放?
实验:
#include<stdio.h>
#include<stdlib.h>
int main(){
int *a=malloc(4);
*a=78;
printf("%d\n",*a);
realloc(a,8);
*(a+1)=99;
printf("%d %d",*a,*(a+1));
}
结果:
所以重新分配内存不会释放掉之前的内存,而是在原内存的基础上添加新的空间,而且如果重新分配的内存比之前的空间小,并不会截断之间后面的内存,还是可以访问的
#include<stdio.h>
#include<stdlib.h>
int main(){
int *a=malloc(8);
*a=78;
*(a+1)=99;
realloc(a,4);
printf("%d %d",*a,*(a+1));
}
问题2:
如果只分配4字节的空间,但是强制使用大于4字节的空间,能正常存取吗?
#include<stdlib.h>
#include<stdio.h>
int main(){
int *a=malloc(4);
for(int i=0;i<4;i++){
*(a+i)=i+4;
}
for(int i=0;i<4;i++){
printf("%d ",*a);
a++;
}
}
输出
存取成功!也就是说,实际分配的空间不管多大,当指针移动时,都是按数据的类型移动的,并按数据类型对齐,所以能正确的存取数据
看调试过程,a的地址是每次增加4的
如果不用alloc分配内存,直接用一个指针呢?
#include<stdlib.h>
#include<stdio.h>
int main(){
int *a;
a++;
a++;
*a=1;
printf("%d ",*a);
}
断点调试时可以看到a的地址是0x10,每次a++,地址+4
每次a++的反汇编代码是add指令
在对*a赋值时出现Segmentation fault
异常,也就是不能对*a赋值
segmentation fault:随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump.
查看反汇编的内存地址
可以看到0x3fffff以前的内存是无法访问的,从0x400000才是程序能用的地址
猜测:因为内存的分段:
0x3fffff之前的内存空间都是属于kernal的,所以不能用
那如果给指针初始化一个可使用的内存空间呢?
#include<stdlib.h>
#include<stdio.h>
int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
}
成功,这也是正常的调用
断点调试看内存地址:
问题3:
如果给指针初始化一个int型的地址,它会自动对齐后续的空间吗?
若继续后移指针,并存数取数:
#include<stdlib.h>
#include<stdio.h>
int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
a++;
*a=2;
printf("%d ",*a);
}
结果只输出了一个,但也没有报错,再断点调试看看:
可以看到在后移指针后,在给*a赋值,a的地址发生了改变,也就说*a=a,a指向本身?
验证:
#include<stdlib.h>
#include<stdio.h>
int main(){
int z;
int *a=&z;
*a=1;
printf("%d ",*a);
a++;
printf("%d %d",a,*a);
}
尝试*a=&x
正常情况下,如
int *a;
int x;
*a=&x;
这样是错误的,会报异常:assignment to 'int' from 'int *' makes integer from pointer without a cast,即从“int *”赋值到“int”会使指针中的整数没有强制转换,因为a是一个int型指针,*a得到的应该是一个int型,&x则是一个int *,这个操作相当于把一个int *赋值给一个int
但是由上个问题实验得知,初始化后的指针后移会指向本身,所以int是可以存int*的
实践:
#include<stdio.h>
int main(){
int z=1;
int *a=&z;
a++;
int x;
*a=&x;
*a=4;
printf("%d",*a);
}
虽然有warning,但是成功输出了,证明了此时a就是指向本身,同时也证明了c语言中的数据类型是互用存储的,只要字节长度相同就能存,int ,int *只是一个规定的标签罢了
运行完*a=&x后,a的值变成了x的地址
问题4:
如果只是在int *前有int型变量初始化,那么指针会自动对齐
如:
#include<stdio.h>
int main(){
int a=1;
int *p;
*p=3;
p++;
*p=5;
}
调试过程:
而且可以看到,指针后移之后,*p被初始化成了0
问题总结
alloc分配内存时会自动对齐,即使分配的空间很小,也能继续使用空间
int a;如果不赋值的化,就会被编译器优化掉
直接对指针操作,无法赋值,因为初始它指向的是不能访问的内核空间
如果给指针赋值&a,并不会自动对齐后续的空间,p++得到的是&a++,实际上,p++后指针指向本身
但是如果给变量赋了初值,int a=1;之后对指针直接操作,是自动对齐的,且,p++后的*p会初始化成0
以上探究只是为了搞清楚内存分配的实质,但在项目中不要随便用,这些可能属于未定义行为,很容易导致异常
标签:字节,int,++,内存,补缺,include,温故,指针 From: https://www.cnblogs.com/Tenerome/p/Creview14.html