C/C++内存分布
内存分布示意图:
内存分布各区域详解:
内核空间:
放置操作系统相关的代码和数据。(用户不能直接进行操作 ------ 可以通过调用系统提供的 api 函数)
栈区:
又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的(向低地址增长)
特点:由编译器自动管理(函数结束时自动释放),大小有限(默认几MB,可能溢出)
内存映射段:
是高效的I/O映射方式,用于装载一个共享的 动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
堆区:
用于程序运行时动态内存分配(malloc/new),堆是可以向上增长的(向高地址增长)
手动申请和释放(容易泄漏或碎片化)
未初始化数据段(.bss):
未初始化或初始化为0的全局变量和静态变量
权限:可读可写
已初始化数据段(.data):
已初始化的全局变量和静态变量(非零初始值)
权限:可读可写
只读数据段(.rodata):
存放只读常量(如字符串常量、const全局变量)
特点:权限是"只读",运行时不可修改(修改会导致段错误)
以上三个数据段在某些内存分布中会划分到一个区域"数据段"中
代码段(.text):
编译后的机器指令(二进制代码)即可执行代码,可能包含一些只读常量(某些编译器将只读常量放在.rodata中)
注意事项:
1. 栈VS堆:
- 栈:速度快,自动管理,但空间有限
- 堆:灵活(大空间),但需要手动管理
2. 指针与内存:
- 指针变量本身存放在栈或堆,但其指向的数据可能在任意区域
3. 只读数据段(.rodata)和代码段(.text):
- 只读数据段和代码段在内存分布的地址接近,并且都是只读不可修改的,所以有些内存分布也会将其划分为一个区域——代码段,即代码段也会存放只读常量
4. 规律:
- 其实可以看出未初始化数据段(.bss)和已初始化数据段(.data)只和全局/静态变量相关,只读数据段也和静态变量相关,所以当给你一个变量让你说出它在内存中的位置,只要是全局/静态的,就在这三个数据段中的一个,具体哪个段根据具体情况分析
- 局部变量只要不涉及静态基本都在栈中
5. 不同的内存分布图:
- 不要过分纠结,会出现这样的情况主要是有时候会因为某些区域的特性一致以及位置上相邻,就会将其粗划分为一个区域,比如有时会将未初始化数据段(.bss)和已初始化数据段(.data)划分到"数据段"这一个区域,还有比如上面的代码段和只读数据段(.rodata)划分到一个区域这样
- 还有一些是因为从不同的视角去划分,比如像,如果是编译器和连接器视角的可执行程序文件内存布局,那么就没有内核空间、内存映射段这两个区域,但如果是操作系统视角就有这两个区域
常见的内存分布图:
像上面的内存分布图,其实可以看出全局区(有些也叫做静态区)其实就把未初始化数据段(.bss)和已初始化数据段(.data)划分为一个区域了,常量区也就是只读数据段(.data)的另一种叫法,其余的代码区、栈、堆都和前面说的内存分布中一个意思
像这个内存分布其实就是将未初始化数据段(.bss)和已初始化数据段(.data)划分为"数据段"一个区域了,将只读数据段(.data)和代码段(.text)划分为"代码段"一个区域了
还有其他的分布方式这里就不一一列举了,只要知道文章一开始我们给出的内存分布图各个区域存储的内容和特点,其他的分布图其实都能理解
最后举个具体的题目来测试一下你的学习成果吧:
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1); free(ptr3);
}
选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____
staticGlobalVar在哪里?____
staticVar在哪里?____
localVar在哪里?____
num1 在哪里?____
char2在哪里?____
*char2在哪里?___
pChar3在哪里?____
*pChar3在哪里?____
ptr1在哪里?____
*ptr1在哪里?____
答案:C C C A A A A A D A B
额外补充一些知识:
为什么把程序的“ 代码段 ”和“ 数据段 ”分开存放?
- 当程序被装载后,数据和指令分别被映射到两个虚拟内存区域。数据段对进程来讲是可读写的,而代码段对进程来说是只读的,所以这两个虚拟内存区域的权限可以被分别设置为可读写和只读,防止程序的指令被有意和无意地改写。
- 现代CPU的缓存一般被设计成数据缓存和指令缓存分离,程序的指令和数据被分开存放对CPU的缓存命中率提高有好处。
- 当系统中运行着多个该程序的副本时,例如多个线程同时都运行同一个程序,它们的代码段指令都是一样的,所以内存中只需要保存一份该程序的代码段,然后将每个副本进程的数据段区域分来,这样可以节省大量空间。