【基础】C程序内存布局及变量存储问题

【基础】C程序内存布局及变量存储问题

  • C程序内存布局
    • 静态区
      • 文本段
        • 代码段
        • 只读常量区
          • 字符串问题
          • const常量问题
      • 数据段
        • static静态变量
      • BSS段
    • 动态区

C程序的内存布局中涉及的数据段、文本段、BSS段等等,与此关联的映像、运行状态,以及静态、全局、局部变量的存储位置和生命周期……这些都是一些非常基础但是又容易被忽略的知识点,正好最近有接触到所以决定好好梳理一下。以下部分内容和图片来自博客:C程序的内存布局(Memory Layout)

C程序内存布局

【基础】C程序内存布局及变量存储问题_第1张图片

静态区

静态区由许多部分组成,静态区的大小在程序运行后不会发生变化。

文本段

根据我看到的解释,有些地方也将它称作只读段

代码段

代码段显而易见就是程序的指令。一个程序被编译并且开始运行后,代码是不可以修改的。

只读常量区

字符串问题

根据名字也可以知道是存放常量的地方,常量只可读但不可写。最常见的就是字符串了。
举例:
在某一个函数中有如下语句:

char* str="example"

这一句有很多陷阱,
首先,"example”是一个字符常量,所以实际的字符串是被保存在常量区;
其次,由于是在函数中创建的char型指针,所以该指针实际上存储在栈区。在编译的时候,这段代码会在只读常量区分配一段空间给字符串,在实际运行时会将字符串的首地址赋给局部变量str。
这里有一个隐含的问题就是,无法通过*str='a’去修改字符串,因为字符串本身存放在只读常量区,但是这句代码也不会出错,原因是它并不像我们所想的那样修改了字符串的值,而是将str指针重新指向了一个新的字符。至于该字符是否存放在哪里需要确定。和朋友讨论后认为应当是作为立即数存放在代码段中。
举例2
在某一个函数中有如下语句:

char str[]="example"

这一句就更骚了
首先,"example”是一个字符常量,所以实际的字符串是被保存在常量区;
其次,在函数中创建了一个局部变量char型数组,实际运行时,会根据字符串的实际长度,在栈中分配一个数组空间,并且将常量区的字符串复制到栈中。
由于当前的数组成员是存放在栈中的,所以是可以修改数组的成员的,当然,字符常量本身还是没有被修改。

const常量问题

这里还有一个非常重要的点:使用const声明的变量是否存放在只读常量区
如果使用const声明一个全局变量,可以知道的是const全局变量在声明时一定要进行初始化否则后面就不能赋值了因为该变量保存在只读常量区。
但是如果在函数中声明一个const型变量会怎么样?在网上查了很多资料后发现,这种局部常量并不像全局常量存放在常量区,而是和其他局部变量一样存放在栈区。在编译时会检查该变量是否被修改了,如果有就会报错,也因此实现了局部常量的只读性。

数据段

这里很明显就是存放可读写的全局变量和静态变量的地方。在实际编程时,如果在源文件里直接声明一个变量,它就是一个全局变量,会被存放在数据段中。只要尝试一下在同一个工程的两个源文件中定义名称一样的变量就会报错。因为最终会被链接成一个可执行文件,所有的全局变量都会一起存放在数据段,如果名称相同还如何区别?

static静态变量

static是一个非常常见的关键字,它的作用有两个方面:
1.如果使用static修饰全局变量,就会限定该全局变量的作用域只在自己所在的源文件中;所以如果两个源文件中名称相同的全局变量都是用static修饰,是否会报错以及如何区别它们有待确认:不会报错,实际上既然使用了static修饰,在两个源文件中就是完全无关的变量,在最终也会变成数据段中两个不同的地址。
2.如果使用static修饰局部变量,该局部静态变量就不会像其他局部变量一样存放在栈区,而是存放在数据段中,也因此能够实现函数已经执行完成返回后,所有的局部变量都释放了而局部静态变量仍然存在。

BSS段

存放的是未初始化或者初始化为0的全局变量或静态变量。BSS段在编译以及实际运行前,是没有被分配实际的空间的,而只是将应当存放在BSS段的变量的类型和名称存放起来,等到程序运行前的初始化才会进行分配。换一个角度来看就是,一个程序的可执行文件存放在硬盘中时,是没有为BSS段分配实际的大小的,只有当程序加载到内存中开始运行前才会分配实际的空间。
举个例子:

…………………………
char array1[1024]={0};	//定义一个全部变量并且初始化为0
int main(void)
{
}
…………………………

根据定义可以知道array1数组实际上被存放在BSS段中,但是BSS段并不会在程序运行前分配空间,所以程序实体的大小不会有太大变化。但是还是会有变化毕竟要存放array的信息。如果是下面的代码:

…………………………
char array1[1024]={1};	//定义一个全部变量并且初始化为1
int main(void)
{
}
…………………………

这段代码就会让程序实体变大1M多,因为该全局变量存放在data段,并且实际就分配了1024个字节的空间。其实会想到一个问题,为什么要区别BSS段和DATA段,其实也比较好想,为了优化程序的大小节约存储空间。因为BSS存放的都是未初始化或者初始化为0的变量,那我何必要一开始就为他们分配好那么多存放着0的空间呢?既然知道它们在程序开始运行时都是0,那干脆记录一下他们的信息然后等运行的时候再分配空间给他们就好了。
这里还涉及到字符表之类的知识,还需要补充学习

动态区

动态区当然就是指在程序运行的过程中,实际大小会动态地变化。

堆主要是由程序员自己使用,通过调用malloc以及free函数进行申请和释放。

栈空间是由程序运行时自动分配的,主要用于函数调用时的现场保存与恢复,参数传递,局部变量等。

【基础】C程序内存布局及变量存储问题_第2张图片
上图为程序在内存中运行时的布局。一个可执行程序分为映像和运行两种状态。在编译链接后形成的映像中,将只包含文本段、和数据段,展开说也就是只包含代码段、只读数据段、数据段。在程序运行之前加载的过程中,将动态生成未初始化数据段(BSS),也就是先前说的BSS段的生成。在系统中,可执行文件(或者程序)最终只有放置在内存中才能运行的,程序的几个段,最终也会转化为内存中的几个区域。

你可能感兴趣的:(C/C++,内存布局)