原文链接
C语言内存空间管理
一、C语言空间管理
内存
物理内存是最终数据的归属地,编程到最后一定是要操控物理内存的。
裸机编程:直接操作硬件的编程。单片机、MCU编程。这种对硬件编程的可移植性非常差。
带操作系统
操作系统给C语言提供了“假的内存”,即虚拟内存。OS隐藏了不同机器的硬件资源,可以为程序员屏蔽硬件细节。
虚拟内存的大小由操作系统的位数决定。
- 空间管理:操作系统对相关的 虚拟内存区域的 分段的行为
- 空间有多大:跟操作系统的寻址能力,即操作系统的位数有关
空间管理的逻辑、分段 segment
分了哪些段
- 操作系统内核段:不允许访问,由操作系统保护,一旦访问会被操作系统杀掉程序,OS会提供系统调用来访问这个区域。
- 栈段:存放临时变量(局部变量),即函数被调用后,它内部申请的数据。函数一旦返回,那么刚才放到这个区域里面的变量就会被“弹出”。
- 堆段:程序提供一个完全由程序员来决定空间生命周期的一个区域。
- 数据段:存放全局变量等。生命周期从程序运行开始,程序终止结束。
- 代码段:函数里面写的所有的指令,是只读的,一写就出错
4G -------------
操作系统内核段 不许访问 操作系统保护 被操作系统杀掉这个程序 OS提供系统调用
3G -------------
栈段 存放临时变量 局部变量 函数被调用后,该函数内部申请的数据,函数一旦返回
刚刚放到这个区域里的变量,就被“弹出”
堆段 生命周期由程序员来管理 malloc free
数据段 全局变量 生命周期:程序运行start 程序终止end
// 下面这些段了解就行
.data 初始化的全局变量 静态变量 区域
fun2_data //假设data是fun2和fun3的静态变量,编译器会帮我们重命名它们
fun3_data
.bss 未初始化全局变量 静态变量 区域
.rodata 只读数据段 "hello world" 一写就出错
代码段 函数里写的所有的指令 只读 一写就出错
small ------------- 非常非常小的一块区域
OS保护区 不许访问 读写都不行 被操作系统杀掉这个程序
0G -------------
static关键字
静态区关键字,把这个变量不在存放默认区域里,只存放在数据区,帮你重命名fun3_data
int *fun3() {
static int data[10]; // 告诉程序,要想通过data访问内存,只能在这个区域里面才能访问
return data; // 返回后,可以通过地址访问
}
每个段里存放数据的生命周期(一定要知道)
a.栈
- 函数调用后,变量申请在栈上
- 函数返回后,变量从栈上“消失”
- 栈段有大小限制,如果函数中的局部变量非常多,会导致栈溢出
b.堆
- 生:调用
malloc
后,操作系统会在堆段中找一个没有被使用的区域,标记出来,返回首地址。 - 死:将堆区的这个首地址交给free的函数后,操作系统就会去堆区里面找它,然后把它标记为“未使用”。如果这个首地址再交给free,此时操作系统会抛出
double free
的异常(会让程序直接死掉)。 - 死:程序退出。如果堆中只申请,不释放,那么它会导致内存不足。如果再申请,会返回NULL,所以使用malloc时得判断一下是不是为空。
malloc
的申请单位是按照字节申请。
int *fun1(int n) {
int *p1 = (int *)malloc(sizeof(int) * n); // 申请5个int空间,返回这个空间的首地址。由于malloc返回的地址并没有类型,所以前面要加一个(int *)来强转,拿该类型的指针来接收
struct abc *p2 = (struct abc *)malloc(sizeof(struct abc) * n);
// 一旦执行到return , p1、p2会消失,而它们申请的空间却没有消失
// 此时,整个系统,都没有人找得到它们申请的空间,所以要在p1、p2消失前,把这个信息通过函数的返回值给上层
return p1;
}