堆内存管理
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);
初始化值