C语言动态内存空间的分配
上节我们讲解了C语言的内存模型,了解到堆区的内存空间是由程序员来分配和释放的,称为自由区,其他区域一般不能由程序员随意操作。本节要讲解的动态内存分配就是在堆区进行的。
动态内存分配和释放常用到的四个函数为:malloc()、calloc()、realloc() 和 free()。
这几个函数的具体用法在C标准库中已经进行了讲解(点击上面链接查看),这里不再赘述,仅作简单的对比,并给出一个综合示例。
作用:在堆区分配 size 字节的内存空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:分配内存在动态存储区(堆区),手动分配,手动释放,申请时空间可能有也可能没有,需要自行判断,由于返回的是void*,建议手动强制类型转换。
功能:在堆区分配 n*size 字节的连续空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:calloc() 函数是对 malloc() 函数的简单封装,参数不同,使用时务必小心,第一参数是第二参数的单元个数,第二参数是单位的字节数。
功能:对 ptr 指向的内存重新分配 size 大小的空间,size 可比原来的大或者小,还可以不变(如果你无聊的话)。
返回值:成功返回更改后的内存地址,失败则返回NULL。
功能:释放由 malloc()、calloc()、realloc() 申请的内存空间。
注意:每个内存分配函数必须有相应的 free 函数,释放后不能再次使用被释放的内存,建议在 free 函数后把被释放指针置为 NULL,好处有二:
另外,在分配内存时最好不要直接用数字指定内存空间的大小,这样不利于程序的移植。因为在不同的操作系统中,同一数据类型的长度可能不一样。为了解决这个问题,C语言提供了一个判断数据类型长度的操作符,就是 sizeof。
sizeof 是一个单目操作符,不是函数,用以获取数据类型的长度时必须加括号,例如 sizeof(int)、sizeof(char) 等。
下面的例子演示了如何用 malloc() 和 sizeof 分配内存空间来保存20个整数:
最后是一个综合的示例:
ip[0] = 0 ip[1] = 1 ip[2] = 2 ip[3] = 3 ip[4] = 4
large_ip[0] = 0 large_ip[1] = 1 large_ip[2] = 2 large_ip[3] = 3 large_ip[4] = 4 large_ip[5] = 9 large_ip[6] = 9
small_ip[0] = 0 small_ip[1] = 1 small_ip[2] = 2
代码说明:
1) 代码看似很长,其实较为简单,首先分配一个包含5个整型的内存区域,分别赋值0到4;再用realloc函数扩大内存区域以容纳7个整型数,对额外的两个整数赋值为9;最后再用realloc函数缩小内存区域,直接输出结果(因为realloc函数会自动复制数据)。
2) 这次把分配函数与验证返回值验证写在了一起,为的是书写方便,考虑到优先级问题添加了适当的括号,这种写法较为常用,注意学习使用。
3) 本例free函数只用释放small_ip指针即可,如函数介绍中注意里提到的,另外两个指针已被系统回收,不能再次使用。
动态内存分配和释放常用到的四个函数为:malloc()、calloc()、realloc() 和 free()。
这几个函数的具体用法在C标准库中已经进行了讲解(点击上面链接查看),这里不再赘述,仅作简单的对比,并给出一个综合示例。
1) malloc()
原型:void* malloc (size_t size);作用:在堆区分配 size 字节的内存空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:分配内存在动态存储区(堆区),手动分配,手动释放,申请时空间可能有也可能没有,需要自行判断,由于返回的是void*,建议手动强制类型转换。
2) calloc()
原型:void* calloc(size_t n, size_t size);功能:在堆区分配 n*size 字节的连续空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:calloc() 函数是对 malloc() 函数的简单封装,参数不同,使用时务必小心,第一参数是第二参数的单元个数,第二参数是单位的字节数。
3) realloc()
原型:void* realloc(void *ptr, size_t size);功能:对 ptr 指向的内存重新分配 size 大小的空间,size 可比原来的大或者小,还可以不变(如果你无聊的话)。
返回值:成功返回更改后的内存地址,失败则返回NULL。
4) free()
原型:void free(void* ptr);功能:释放由 malloc()、calloc()、realloc() 申请的内存空间。
注意:每个内存分配函数必须有相应的 free 函数,释放后不能再次使用被释放的内存,建议在 free 函数后把被释放指针置为 NULL,好处有二:
- 再次访问该指针将出错,避免野指针;
- 再次释放该指针不会让程序崩溃只是free函数失效。
对比与说明
在利用 calloc() 函数时,如果对分配的存储空间不保存,那么丢失后就无法找回来,更严重的是这段空间不能再重新分配,因而造成内存的浪费。因此我们较少使用calloc(),推荐使用malloc()。另外,在分配内存时最好不要直接用数字指定内存空间的大小,这样不利于程序的移植。因为在不同的操作系统中,同一数据类型的长度可能不一样。为了解决这个问题,C语言提供了一个判断数据类型长度的操作符,就是 sizeof。
sizeof 是一个单目操作符,不是函数,用以获取数据类型的长度时必须加括号,例如 sizeof(int)、sizeof(char) 等。
下面的例子演示了如何用 malloc() 和 sizeof 分配内存空间来保存20个整数:
int *numbers = (int*) malloc( 20 * sizeof(int) );这种分配内存的方式在数据结构中很常见。
最后是一个综合的示例:
#include <stdio.h> #include <stdlib.h> #define N (5) #define N1 (7) #define N2 (3) int main() { int *ip; int *large_ip; int *small_ip; if((ip = (int*)malloc(N * sizeof(int))) == NULL) { printf("memory allocated failed!\n"); exit(1); } int i; for(i = 0; i < N; i++) { ip[i] = i; printf("ip[%d] = %d\t", i, ip[i]); } printf("\n"); if((large_ip = (int* )realloc(ip, N1 * sizeof(int))) == NULL) { printf("memory allocated failed!\n"); exit(1); } for(i = N; i < N1; i++) large_ip[i] = 9; for(i = 0; i < N1; i++) printf("large_ip[%d] = %d\t", i, large_ip[i]); printf("\n"); if((small_ip = (int*)realloc(large_ip, N2 * sizeof(int))) == NULL) { printf("memory allocated failed!\n"); exit(1); } for(i = 0; i < N2; i++) printf("small_ip[%d] = %d\t", i, small_ip[i]); printf("\n"); free(small_ip); small_ip = NULL; system("pause"); return 0; }运行结果:
ip[0] = 0 ip[1] = 1 ip[2] = 2 ip[3] = 3 ip[4] = 4
large_ip[0] = 0 large_ip[1] = 1 large_ip[2] = 2 large_ip[3] = 3 large_ip[4] = 4 large_ip[5] = 9 large_ip[6] = 9
small_ip[0] = 0 small_ip[1] = 1 small_ip[2] = 2
代码说明:
1) 代码看似很长,其实较为简单,首先分配一个包含5个整型的内存区域,分别赋值0到4;再用realloc函数扩大内存区域以容纳7个整型数,对额外的两个整数赋值为9;最后再用realloc函数缩小内存区域,直接输出结果(因为realloc函数会自动复制数据)。
2) 这次把分配函数与验证返回值验证写在了一起,为的是书写方便,考虑到优先级问题添加了适当的括号,这种写法较为常用,注意学习使用。
3) 本例free函数只用释放small_ip指针即可,如函数介绍中注意里提到的,另外两个指针已被系统回收,不能再次使用。