【C/C++】内存分配与释放(malloc、calloc、realloc、free)
本文结构
内存区域
在程序的执行期间经常需要动态的分配内存。
具体表现有在函数体内声明了一个变量、结构体或数组等,这样的内存是分配是由系统操作分配在栈上的,在执行完函数后,函数体内开头所声明的变量、结构体或数组所持有的内存空间都会被释放。所以要将函数体内的执行结果返回或反映到函数体外,一般是行不通的(不考虑全局变量)。
还有一种是由coder们调用malloc()
等内存分配函数在堆上开辟新内存块,这些内存块会一起存在直至调用free()
函数去释放。作用域广了,但也引入了潜在的内存泄露(程序卡顿)和野指针(程序crash)问题。
函数讲解
#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
引入stdlib.h
头文件后可以在代码中操作内存的分配与释放。
下面来一一讲解下内存分配函数:
- void *malloc(size_t size);
malloc()
函数分配了指定大小为size
的字节数,这些字节块是没有被初始化的,也就是说有可能是非\0
有值的。这也正是malloc()
的缺点。如果size
为0,则函数返回NULL
;否则返回一个指向该内存块的指针,该指针可随后被free()
调用释放。
函数的返回类型是void *
,即任意类型,虽然很多编译器会把malloc()
返回的地址自动转换成赋值语句左边的指针类型,但在编码中,习惯在malloc()
赋值前做一次强制转换。
另外,由于可能在执行内存分配时刚好内存不足,函数也会返回一个NULL
,所以对所返回的指针做非空判断。如下所示:
char* p = (char*)malloc(10 * sizeof(char));
if (p == NULL) {
// handle null error
} else {
// go on
}
- void *calloc(size_t nmemb, size_t size);
calloc()
解决了malloc()
分配的内存块无初始化的问题,每个字节都被强制赋值为\0
。还有另一个特点是把内存块分配为给定了大小的数组。即第一个参数nmemb
指定了分配的元素个数,第二个参数size
指定了单个元素的指定大小。
刚才malloc()
的示例代码可以使用calloc()
的话则可修改为:
char* p = (char*)calloc(10, sizeof(char));
if (p == NULL) {
// handle null error
} else {
// go on
}
- void *realloc(void *ptr, size_t size)
在程序运行中,碰到新的数据需要继续填充处理时发现原来的内存块已经不够了,则要开辟新更大的内存块以处理更多的数据,则就需要使用到realloc()
函数了。realloc()
函数的第一个参数ptr
表示指向老的内存块的指针,第二个参数sizt
则表示新的内存块大小。
开辟出新的内存块后,会把老内存块的数据复制到新内存块中;老内存块的空间会自动被系统回收。
所以如果新开辟的内存块比较老的内存块大,则前部分是老内存块的数据,后半部分则是未初始化的数据,即内容具有不确定性;如果新开辟的内存块比老的内存块小,则相当于复制了老内存块前半部分的内存到新的内存块中去了;
函数正常执行时,返回新内存块的首地址指针;如果函数执行失败,则返回NULL
。
如果指定的ptr
是NULL
,则函数的作用相当于malloc(size)
;
如果指定的size
值为0,则相当于调用了free(ptr)
去释放了原有的内存块;
- void free(void *ptr)
释放指针ptr
所指向的内存块。如果ptr
是NULL
,则相当于什么也没做。
当对ptr
执行free()
操作后,要及时对ptr
赋值为空,避免野指针的出现或误操作。
realloc的内存泄露陷井
说是内存泄露陷井,其实是想说一个编码不规范的现象。挺多人在使用realloc()
是这么写的:
char * p = (char *)malloc(10 * size(char));
... ...
// 发现内存不够用了
p = realloc(p, 20 * size(char));
这样子的代码,如果realloc()
因某种原因执行失败导致返回NULL
对p
赋值,则失去了指向原先分配的10 * size(char)
这个老内存块的指针,该老内存块再也无法通过free()
函数释放了,导致内存泄露。
正确的写法应该是这样子的:
char * p = (char *)malloc(10 * size(char));
... ...
// 发现内存不够用了
char * tmp = realloc(p, 20 * size(char));
if (tmp == NULL) {
// realloc() do failed. do some action
return; // 这里选择return,也可以再尝试realloc一次或其它
} else {
p = tmp;
}
// go on do something...
示例代码
以下代码演示从命令行终端中不断获取输入字符的过程。
由于一开始内存分配仅分配够存储两个字符,当执行操作时就会出现空间不足需要调用realloc()
再开辟更大的空间。
其中示例代码中的函数get_string()
见sodino另一篇文章【C/C++】使用getchar()实现gets()功能
1 |
|
运行效果如下图: