写好了代码只是第一步,接下来还需要编译生成对应的二进制才能使用(预处理,编译,汇编,链接)。
那么在运行的时候,代码和数据在内存中都是怎么分布的呢?c的内存布局是怎样的呢?c++的内存布局是怎样的呢?
有一点值得注意,C语言和C++的内存布局是不一样的,这也就是平日里搜索c++内存布局的文章内容总是很相似但还不相同的原因,有些人没有对比清楚两者区别!我以前也是这样的。
C内存布局
c语言内存布局分为5部分:初始化数据段,未初始化数据段,代码段、堆区和栈区
- 初始化数据段分为只读数据和读写数据。
- 只读数据如初始化的被const修饰的变量或者一些常量,比如`char *str = "abc";`,常量"abc"就存储在只读寄存器中。如果定义成`char str[] = "abc"`,代表的是一个数组,可能定义在栈上,也可能定义在初始化数据段的读写数据中,主要看定义的位置。
- 读写数据存储被定义了初始值的一些变量,包括初始化过的全局变量和被static修饰的静态变量
- 未初始化数据段存储未被初始化的变量,也称之为BSS段,即Block Started by Symbol。未被初始化的变量被默认初始化为0,包括全局变量和被static修饰的静态变量
- 代码段存储代码编译出来的二进制,程序执行的时候由依次执行
- 堆区存储程序中动态申请出来的空间(malloc出来的,注意free释放内存,否则会出现内存泄漏问题)
- 栈区存储程序中的局部变量。堆与栈的生长方向相反:栈在内存中也是一种数据结构中栈的样子,是向地址小的方向生长,在linux中可以查看栈的大小`ulimit -s`;堆在内存中的结构如同数据结构中的堆,向着方向大的地方生长,堆比较大,比较接近计算机内存的大小。
C++内存布局
C++的内存布局同样分为5个部分:
- 常量存储区:存储常量字符串,如同C语言内存布局中的初始化数据段的只读数据。
- 全局变量/静态存储区:如同名字,存储这两类数据,不再区分是否初始化,更加直观容易理解了。包括全局变量,静态变量和常量
- 代码段:同C语言中代码段
- 堆区:因为在c++中多了new这个操作符,在堆区中也区分为new的堆区和malloc的堆区。new出来的空间在程序退出时候,操作系统帮忙清理。
- 栈区:同C语言中栈区
由内存布局的差异而引发出来的问题
- 静态变量的问题
- 静态变量只在当前文件中生效,其他文件中是看不见的。如果想要在文件中看到另一个文件中的变量,需要使用extern关键字进行修饰,表示该变量已经在其他文件中声明了,此处只是声明一下区通过编译,在link阶段会找到定义的位置进行链接。
- 静态局部变量和静态变量,通过static修饰的变量会扩大变量的生命周期,在程序运行的阶段一直存在;而局部变量是存储在栈上的,当当前函数(block)退出后,里面的局部变量就会被退栈释放掉
- new和malloc的区别
- 堆和栈的区别
参考文章:
- http://harlon.org/2018/04/21/cpluscplusmemory/
- https://blog.csdn.net/iFuMI/article/details/52689373
- https://blog.csdn.net/cherrydreamsover/article/details/81627855