以下是内存分布图。
我们可以自己在堆区申请,释放空间。其函数头文件为#include<stdlib.h>
malloc 函数介绍
1.malloc函数原型,默认返回void*,参数是要申请多少个字节
2. 函数返回值
若开辟成功,则返回被分配的内存的指针(也就是指针有了地址,不是野指针),
若不成功,则返回空指针NULL
但注意,返回的void*指针无法解引用,所以要将其强制类型转换成我们所需要的指针类型,再用一个指针接收
例如: int* p = (int*) malloc(40)
3.使用的注意事项
空间用完以后记得要用free释放空间,并且将用于接收的指针指向空指针NULL,
因为free会将空间的内容释放,也就是之前的内容不存在了,但是指针p依然记住了地址,如果后续用到的指针p就是在使用野指针,会越界访问,出现问题。
calloc函数介绍
1.calloc函数原型
与malloc不同的地方在于参数,calloc(需要申请的个数,每个的占的字节数)
void* calloc(size_t num ,size_t size)
int* p = (int*) malloc(4*sizeof(int)) 相当于 int* p = (int*)calloc(10,sizeof(int))
与malloc不同的第二个地方在于,calloc函数会将开辟的空间初始化为0,malloc则不初始化
realloc函数介绍
1.函数作用
在已经用malloc或calloc开辟的空间上,再添加空间
2.realloc函数原型
第一个参数为已开辟空间的地址,第二个参数为原来空间的大小+还需开辟的大小(字节)
3.使用注意事项
realloc使用的时候有三种情况
1.realloc 时其地址后的空间够用,如图
2.realloc 时其地址后的空间不够用,因为可能空间已经被开辟使用,如图
所以此时就要另寻空间,如图
可以看到realloc函数放弃了先前的地址,在箭头所指的合适的空间,把原有的内容拷贝过来,在其后增加我们想要的空间,然后把先前的地址free掉,返回箭头所指的地址。
这两种情况用以下代码是没问题的,因为反正realloc函数返回的都是有效地址
int main()
{
int* p = (int*)malloc(40);
p = (int*)realloc(p, 40);
return 0;
}
3. realloc 时其地址后的空间不够用,因为空间已经被开辟使用,并且在别处也找不到合适的空间,如图
realloc找不到合适的空间,于是调整大小,并返回一个空指针NULL,这时候我们再用上面的方法接收就十分危险,因为不仅开辟空间失败,还把原来的地址变成NULL,丢了西瓜也丢了芝麻。
因此用以下方法,先对realloc返回值是否为空指针进行判断,就显得更加安全。
------------------------------------------------------分割线----------------------------------------------------------------
小练习
想要避免此类错误,牢记以下格式
1.malloc开辟空间
2.创造指针接收,例如:int* p = (int*) malloc(40)
3.使用完后记得free,free(p)
4.指针也要置空,p = NULL
第一题:
错误1:因为传的是str,不是传str的地址,相当于传值,p只是str的一份临时拷贝,不会影响到str
错误2:指针p用完没有free
正确写法如下图
第二题:
x是局部变量,出了该局部,x所开辟的空间就会立即被free,所以就算会返回局部变量x的地址,如果再去访问该地址就会出问题,因为该地址所代表的空间已经被free了
第三题:
指针ptr没有初始化,是个野指针,后续进行解引用操作就会越界访问。
所以我们用指针的时候,要么就初始化,要么置为NULL
第四题:
补充一个知识点,在栈区的变量出了局部会被操作系统自动free,而在堆区的变量不会(是由程序员自己手动free)
错误:首先,可以看到char p[ ] 是个数组,p在栈区开辟了一块空间。
随后return p,但出了该局部,p所开辟的空间就会立即被free,所以就算会返回局部变量p的地址,如果再去访问该地址就会出问题,因为该地址所代表的空间已经被free了,如下图
修改:Getmemory函数里用上malloc就好,出了局部不会被系统free
第五题:
错误1:指针str没有置为NULL
错误2:free的时机太早,导致越界访问
以上就是个人对一些内存函数的理解,希望对大家有帮助。