0.C++中内存模型是否存在:
https://www.zhihu.com/question/394912597/answer/1227991694
首先它是存在的,常见的C++程序运行时确实会呈现网上说的那种布局。至于具体有几个分段,应该没有具体的规范,属于操作系统、编译器和可执行文件加载器自行实现的范畴。
但是,程序也可以随时忽略内存布局的存在。因为C++程序可以直接操作内存,所以在没有开启数据执行保护(DEP,保证代码区不可写,数据区不可执行)或类似功能的情况下,只要进行精巧的编码,你就可以在程序运行中随意改变内存布局,比如把代码或函数复制到堆区运行,动态扩展栈区大小,在代码区存储数据,甚至可以通过即时编译功能动态生成代码并执行。
C++程序的默认内存布局只是为了方便程序开发,让开发者不用操心内存管理。但是开发者如果觉得有必要,也可以随时接管内存布局。
1.C++内存模型(堆栈全常代 ):
- 堆 heap :
保存程序中动态分配的内存,比如C的malloc申请的内存,或者C++中new申请的内存。堆向高地址方向增长。由new分配的内存块,其释放编译器不去管,由我们程序自己控制(一个new对应一个delete)。如果程序员没有释放掉,在程序结束时OS会自动回收。涉及的问题:“缓冲区溢出”、“内存泄露”
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,而数据结构里的堆一种经过排序的树形数据结构,每个结点都有一个值。 - 栈 stack :
是那些编译器在需要时分配,在不需要时自动清除的存储区。存放局部变量、函数参数。
存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了。
其操作方式类似于数据结构中的栈。 - 全局/静态存储区 (.bss段和.data段) :
全局和静态(static)变量被分配到同一块内存中。
.bss段和.data段属于静态内存分配。
在C语言中,未初始化的和初始化为0的放在.bss段中,初始化的(非零)的非const的全局变量和静态变量放在.data段中;在C++里则不区分了。??
发现程序2编译之后所得的.exe文件比程序1的要大得多。 为什么?
程序1:
int ar[30000];
void main()
{
......
}
程序2:
int ar[300000] = {1, 2, 3, 4, 5, 6 };
void main()
{
......
}
/*
区别很明显,一个位于.bss段,而另一个位于.data段,两者的区别在于:
1. 全局的未初始化变量存在于.bss段中,具体体现为一个占位符,占运行时的内存空间(在程序运行时分配内存空间),而不占文件空间;
2. 全局的已初始化变量存于.data段中,数据保存在目标文件中,data类型的全局变量是即占文件空间,又占用运行时内存空间的;
3..bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);
而.data却需要占用,其内容由程序初始化,因此造成了上述情况。
*/
在采用段式内存管理的架构中(比如intel的80x86系统),bss段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,大多数操作系统,在加载程序时,会把所有的bss全局变量全部清零,无需要你手工去清零。bss段属于静态内存分配,即程序一开始就将其清零了。.bss 节 只有在运行时才会有数据( 也就是说, 编译时没有数据), 因此不是编译时分配 数据, 而是预留运行时的内存空间。
- 常量存储区 (.rodata段) :
存放常量(C中的字符串和#define定义的常量),不允许修改(通过非正当手段也可以修改),用const修饰的全局变量是放入常量区的,但是使用cons修饰的局部变量只是设置为只读起到防止修改的效果,没有放入常量区,可以认为是放在栈区。
1.常量不一定就放在rodata里,有的立即数直接编码在指令里,存放在代码段(.text)中。
2.对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一份拷贝。
3.rodata是在多个进程间是共享的,这可以提高空间利用率。
4.在有的嵌入式系统中,rodata放在ROM(如norflash)里,运行时直接读取ROM内存,无需要加载到RAM内存中。
————————————————
- 代码区 (.text段) :
是编译后代码(如函数)的主体,也就是程序的机器指令。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
代码区的指令中包括操作码和要操作的对象(或对象地址引用)。如果是立即数(即具体的数值,如5),将直接包含在代码中;如果是局部数据,将在栈区 分配空间,然后引用该数据地址;如果是BSS区和数据区,在代码中同样将引用该数据地址。
代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果遇到if/else,goto,则需要使用跳转指令,如果进行递归,则需 要借助栈来实现。
每当一个函数被调用,该函 数返回地址和一些关于调用的信息,比如某些寄存器的内容,被存储到栈区。然后这个被调用的函数再为它的自动变量和临时变量在栈区上分配空间,这就是C实现 函数递归调用的方法。每执行一次递归函数调用,一个新的栈框架就会被使用,这样这个新实例栈里的变量就不会和该函数的另一个实例栈里面的变量混淆。
在linux系统中,程序在内存中的分布如下所示:
text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系 统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化为0或者“”。
根据c/c++对象生命周期不同,c/c++的内存模型有三种不同的内存区域:
自由存储区:局部非静态变量的存储区域,即平常所说的栈
动态区: 用operator new ,malloc分配的内存,即平常所说的堆
静态区:全局变量 静态变量 字符串常量存在位置
而代码虽然占内存,但不属于c/c++内存模型的一部分
2.字符串常量在内存的哪个区域
在常量存储区 (.rodata段),只读不可写。
可以参考《C语言中字符串常量存储在哪里》
void cutspace(char* str)//char* str = string;将字符串地址传递给函数局部变量str
//但是字符串保存在常量区,只读不可写,所以在这段内存区进行写的操作都会报错。
{
char* ptr = str;
while(*ptr)
{
if(*ptr != ' ')
*str++ = *ptr;//报错,中断
ptr++;
}
*str = '\0';
}
int main()
{
char* string = "hello motto";//此时字符串保存在常量区,只读不可写
//char string[16] = "hello motto";//这里字符串被复制到栈空间了,可以进行读写
cutspace(string);
printf("%s",string);
return 0;
}
3.const、static型数据在内存中如何存储
static int val_a = 1 ; // 初始化的静态变量
int val_b = 2 ; // 全局变量
const int val_c = 3 ; // const 全局变量
static int val_d ; // 未初始化的静态变量
int val_e ; // 未初始化的全局变量
int main()
{
static int val_f = 5; // 初始化的局部静态变量
static int val_g; //未初始化局部静态变量
int val_h = 6; //初始化局部变量
int val_i; //未初始化局部变量
const int val_j = 7; //const局部变量
return 0;
}
①static无论是全局变量还是局部变量都存储在全局/静态区域,在编译期就为其分配内存,在程序结束时释放,例如:val_a、val_d、val_h、val_i。
②const全局变量存储在只读数据段(常量存储区 (.rodata段)),编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放,例如:val_c;const局部变量存储在栈中,代码块结束时释放,例如:val_j。
③全局变量存储在全局/静态区域,在编译期为其分配内存,在程序结束时释放,例如:val_b、val_e。
④局部变量存储在栈中,代码块结束时释放,例如:val_h、val_i。
————————————————
原文链接:https://blog.csdn.net/qq_26626709/article/details/51887085
原文链接:https://blog.csdn.net/weixin_41783335/article/details/96030170
https://blog.csdn.net/weibo1230123/article/details/82777510