C语言动态内存分配
- 在进程的地址空间中,代码区、常量区、全局数据区的内存在程序启动时就已经分配好了,它们大小固定,不能由程序员分配和释放,只能等到程序运行结束由操作系统回收。这称为静态内存分配。
- 栈区和堆区的内存在程序运行期间可以根据实际需求来分配和释放,不用在程序刚启动时就备足所有内存。这称为动态内存分配。
- 使用静态内存的优点是速度快,省去了向操作系统申请内存的时间,缺点就是不灵活,缺乏表现力,例如不能控制数据的作用范围,不能使用较大的内存。
- 而使用动态内存可以让程序对内存的管理更加灵活和高效,需要内存就立即分配,而且需要多少就分配多少,从几个字节到几个GB不等;不需要时就立即回收,再分配给其他程序使用。
栈和堆的区别
栈区和堆区的管理模式有所不同:
- 栈区内存由系统分配和释放,不受程序员控制;
- 堆区内存完全由程序员掌控,想分配多少就分配多少,想什么时候释放就什么时候释放,非常灵活。
程序启动时会为栈区分配一块大小适当的内存,对于一般的函数调用这已经足够了,函数进栈出栈只是 ebp、esp 寄存器指向的变换,或者是向已有的内存中写入数据,不涉及内存的分配和释放。当函数中有较大的局部数组时,比如 1024*10 个元素,编译器就会在函数代码中插入针对栈的动态内存分配函数,这样函数被调用时才分配内存,不调用就不分配。我们经常听说“栈内存的分配效率要高于堆”就是这个道理,因为大部分情况下并没有真的分配栈内存,仅仅是对已有内存的操作。
动态内存分配函数
堆(Heap)是唯一由程序员控制的内存区域,我们常说的动态内存分配也是在这个区域。在堆上分配和释放内存需要用到C语言标准库中的几个函数:(个人:这几个函数在stdlib.h头文件中声明)
- 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 newsize);
功能:
- 对ptr指向的内存重新分配size大小的空间,size可比原来的大或者小,还可以不变(如果你无聊的话)。
- 重分配成功旧内存会被自动释放,旧指针变成了野指针。
- 先判断当前的指针是否有足够的连续空间,如果有,扩大ptr指向的地址,并且将ptr返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来ptr所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
返回值:成功返回更改后的内存地址,失败则返回NULL。
4) free()
原型:void free(void* ptr);
功能:释放由malloc()、calloc()、realloc()申请的内存空间。
几点注意
1) 每个内存分配函数必须有相应的 free 函数,释放后不能再次使用被释放的内存。
2) 在分配内存时最好不要直接用数字指定内存空间的大小,这样不利于程序的移植。因为在不同的操作系统中,同一数据类型的长度可能不一样。为了解决这个问题,C语言提供了一个判断数据类型长度的操作符,就是 sizeof。
3) free(p) 并不能改变指针 p 的值,p 依然指向以前的内存,为了防止再次使用该内存,建议将 p 的值手动置为 NULL。
最后是一个综合的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #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; return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
2021-05-01 34. 在排序数组中查找元素的第一个和最后一个位置
2021-05-01 35. 搜索插入位置
2021-05-01 240. 搜索二维矩阵 II
2021-05-01 74. 搜索二维矩阵
2021-05-01 154. 寻找旋转排序数组中的最小值 II
2021-05-01 153. 寻找旋转排序数组中的最小值
2021-05-01 81. 搜索旋转排序数组 II