C语言的堆内存管理以及与栈内存的区别
堆内存管理
2021-5-7
Mushroom
与栈内存的区别之处?
1,分配模式
先简单介绍下堆栈内存到底是存储的什么?
栈内存一般存在两种分配方式静态分配,动态分配
- 静态分配:指的是编译器编译的时候就已经分配好的内存。
- 你可以这样理解:最开始编译之后,静态分配的内存信息存储在了二进制文件信息中,每调用一次,编译器就会自动的分配这些内存。(不是很严谨啦,(●'◡'●))
- 动态分配:指的是随着程序的运行动态的分配的,
- 就比如说当程序运行到了
ptr = (int *)malloc(sizeof(int) * 10)
(注意这里的这个函数申请的空间是在堆内存中的!!),
针对于堆栈的两种动态分配内存的方式:
栈内存的动态分配使用
alloca()
,不过不是很推荐#include <stdio.h> #include <stdlib.h> int main() { unsigned int *a; unsigned int *b; a = (unsigned int *) alloca(sizeof(unsigned int) * 8); b = (unsigned int *) alloca(sizeof(unsigned int) * 8); for (int i = 0; i < 8; i++) { a[i] = 4294967295; b[i] = 286331153; } for (int i = 0; i < 8; i++) { printf("%d\n", a[i]); } return 0; }
内存从低地址到高地址,并且连续,不容易产生内存碎片,这一点非常符合栈内存的分配方式。
虽说不产生内存碎片,但是这容易造成访问越界
#include <stdio.h> #include <stdlib.h> int main() { unsigned int *a; unsigned int * ptr; unsigned int * head; head = (unsigned int *) malloc(sizeof(unsigned int)); ptr = (unsigned int *) malloc(sizeof(unsigned int)); ptr = (unsigned int *) malloc(sizeof(unsigned int)); ptr = (unsigned int *) malloc(sizeof(unsigned int)); a = head; a[3] = 286331153; a[2] = 286331153; a[1] = 286331153; a[0] = 286331153; for (int i = 0; i < 4; i ++) { printf("%d\n", a[i]); } return 0; }
这个程序的连续的空间申请,如果是
alloca()
必然会造成访问越界出现。(不是数组,结果可以当作数组来使用)
- 如果换成堆内存分配呢?
malloc()
- 符合预期,自上而下的分配内存,而且不连续,容易产生内存碎片。
2.生存期
堆内存的生存期是直到其被
free()
主动释放,或者程序终止,内存自动释放。
栈内存的生存期是随着程序的运行自动结束释放的,如
main
函数里面如果使用alloca()
函数或者是int a = 0
,这样的内存申请,其生存期其实都是整个main
生存期,如果说是在函数里面定义申请的内存的话。void function() { int a = 0; int *ptr; ptr = (int *)alloca(sizeof(int) * 10); return; }
这样的内存其生存期就存在于函数调用时的生存期,如果函数调用结束之后,自然而然的释放了,无需手动释放。
3.可获取空间大小
一般来说栈空间可申请的内存相对较小,
Linux下8M(我的就是8M)
windows下大约1M
小总结
栈内存可以动态分配,与一些变量声明,函数调用无异样,但是要注意的是,栈的空间其实是有限的,如果手动的在栈空间分配过多的内存就会导致
stackoverflow(栈溢出)
尽量使用栈存储函数的调用信息,或者是一些大小相对较小的变量的存储,而且由于自动内存释放,使用的时候需要考虑可能无法返回的问题。
堆内存的使用
1.申请
申请的时候大小一般没有限制,但是考虑到堆内存的内存碎片,可能无法分配满,所以也不要一下子分配的内存太大了。
void *__cdecl malloc(size_t _Size);
申请
_Size
个大小的空间,申请成功返回头地址。如果失败则返回NULL
空间内存的数据未初始化,只是得到了这么大的空间
2. 初始化
void *memset(void *s, int c, size_t n);
初始化值🐰需要先获取空间。
ptr = memset(array, 0, sizeof(int) * 5)
给长度为5的数组
array
的五个位置上全部赋值为0
.
void bzero(void *s, size_t n);
与上面不同的是全部赋值为
0
3. 释放
void free(void *ptr);
从对空间中直接释放内存,释放之后指向这块内存的指针就无效了(野指针).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int *array;
array = (int *)malloc(sizeof(int) * 4);
array = memset(array, 1, sizeof(int) * 4);
int *ptr = &array[2]; // 指向array空间的第2号元素
printf("original array[1] is %d\n", array[1]);
free(array); // 释放array
printf("now array[1] is %d\n", *ptr);
printf("now array[0] is %d\n", *array);
return 0;
}
这里使用malloc()
函数分配了空间,然后初始化为0
,然后立即释放掉。
此时内存的数据如下
Before
After
对于这样的指针一定要在第一时间赋值为
NULL
,否则一个不小心就错把这样的随机数据拿去使用了,程序的输出自然就出现了问题。
4.再分配
void *realloc(void *ptr, size_t size);
再分配空间,但是使用的用法可以很花哨。
当size等于
0
的时候,释放内存
ptr
等于NULL
的时候,申请内存当分配的内存过大的时候如果后续的连续空间的内存不够,则会另外找新的空间,此时值会被拷贝过去。
摘录
C语言堆内存 --- 少个G
windows栈大小 --- 程序员攻略
C语言:“堆”和“栈”的七个不同之处 --- 午夜逛街的黛玛