C++中的全局变量 局部变量 常量 静态变量的在内存中的分配

在看书时发现了这几个概念有些混乱,和同学们讨论也觉得这个地方没搞懂,包括网上的一些对于这方面的介绍,让自己更糊涂了。花了几天时间,稍微弄懂了些。

之所以有些混乱,我觉得是基本概念没有搞清楚,我查阅了英文版的C++primer,首先把一些定义搞清楚,所以这件事情告诉我们还是外文原版的书靠谱啊!!

下面截一些c++primer的原文,再加上自己的理解。

C++ has three ways of managing memory for data, depending on the method used to allocate memory: automatic storage, static storage, and dynamic storage, sometimes called
the free store or heap.

C++有三种方法去管理数据在内存中的分配:automatic storage, static storage, 和dynamic storage(有时候被叫做free store或者堆)

第一种automatic storage:通常一个变量定义在一个函数中,用auto修饰符修饰的变量,就在这个存储区中。我们知道一般auto修饰符是可以省略的,也就是说在函数中定义的没有static修饰的就在这个存储区内。而automatic storage通常用一个栈结构来维护。也就是说函数中大多数的变量都维护在这个内存区,也就是我们通常所说的栈中。

第二种叫做static storage:这个存储区在整个程序运行的过程中都存在,有两种方式来声明一个在该存储区的变量,一种是在声明变量前加static,第二种是将变量声明在函数之外,也就是我们常说的全局变量。也就是说,在函数之外声明的变量和函数内用static修饰都属于这个区域。这个部分的数据就是在编译之后生成的.bss段和.data以及.text段,在程序运行时,加载到内存的低地址上。(这里需要说明的时,函数内使用static修饰的变量,虽然在内存的地址,但是是在第一次执行到这部分的代码时才会在低地址创建该变量.)

第三种就是我们常说的堆,这里就不详细说明了。


介绍了这三种存储区域,那我们的常量是属于哪里呢?在c++primer中,关于常量并没有对应直接的存储位置,只是说常量,使用const修饰的,叫做常量,一旦声明我们就不能去修改他了。如果没有特殊说明,那么跟变量的内存对应就是一致的,也就是说,如果该常量定义在函数内,那么在运行过程中,就分配在第一种 automatic storage上,也就是我们常说的栈上,而如果const修饰的声明在函数以外,那么就属于static storage的存储范围了。后面我们会看到,这部分对应在.text段中。


总结一下:我们平时说的全局变量,局部变量,常量,静态变量等只是我们的一种称呼,但是不能从这些称呼上对这些变量和常量进行内存分配上的归类。下面把通常的称谓和实际上的内存对应进行一下对应。

先说明一些称谓的定义:

全局变量:在函数之外声明的,没有static const修饰的

局部变量:在函数内声明的, 没有static const修饰的

常量:使用const修饰的

静态变量:使用static修饰的


从上面的介绍,我们可以知道,全局变量和静态变量都是属于static storage区域,在执行中在数据在全局区域属于.data  .text  .bss段,映射到内存低地址

    局部变量维护在通常所说的栈上

    常量没有专门的存储区域,如果该常量在全局声明,那么存储在.text 区;如果该常量生命在函数内,那么就维护在栈上,如果该函数执行结束,    该常量也会被销毁,只是不能修改


以上就对常量,全局数据  局部数据在内存分配上有了大概的了解了。

下面说一下编译出来的二进制文件的几个段

一个二进制文件,使用size命令会看到.text .data .bss三个段的信息

其中,text是指已编译程序的机器代码,可以理解为代码段。

  data是指已初始化的全局变量

  bss是指未初始化的全局变量

这里的全局变量指的就是上面所提到的static storage区域,也就是说在全局中声明的变量和在函数内使用static声明的区域都在这三个段中。

已初始化和未初始化的内容很好区别,

例如,如果在全局声明一个int a或者在函数中声明一个static int a,就会在未初始化段bss中增加一个4字节的大小

同理,如果在全局声明一个int a=1,或者在函数中声明一个static int a=1,就会在初始化段data中增加一个4字节的大小

(经笔者测试,可能因为对齐的方式,data段每次增长8个字节,bss段每次增长16个字节。也就是说如果在初始化data段声明一个int型,data段可能增加8个字节,或者不会增加。如果增加了8个字节,那么在声明一个int型,data段就不会增加了,就会使用另外4个字节。如果第一次声明int没有增加,在声明一个int,就会增加8个字节。bss段同理,是以16个字节为单位)

在测试过程中,笔者发现一个问题,如果在全局声明一个常量,这个常量是在text段中,而不在另外两个段中。这就解释清楚了,笔者和小伙伴在看其他博客中遇到的一个问题:

#include
using namespace std;
const int a = 11; 
int main()
{
    const int b =22;
    int *ptr;
    ptr = (int*) &b; 
    *ptr = 21; 
}

利用类型强转,骗过编译器b是一个常量,这样执行,是可以通过编译,并且正常执行的,并且修改了b对应内存中的值


#include
using namespace std;
const int a = 11; 
int main()
{
    const int b =22;
    int *ptr;
    ptr = (int*) &a; 
    *ptr = 21; 
}

而这段代码,可以编译通过,但是在执行中报段错误,segement fault错误,原因就在于全局的地址是在text段中,这个段是只读的,所以访问这个区域就会有段错误发生


=======================================================================================

使用size命令可以看到可执行文件中每个段分配的数据的大小

Text:表示代码段,也有一些常量在其中(.rodata也在这个部分)

Data:初始化的全局变量保存在这个部分

Bss:未初始化的全局变量保存在这个部分,这部分没有进行初始化,所以不需要分配真正的空间,只需要保存这个变量的符号,当执行时候将其初始化为0

 

 

全局初始化的变量

全局未初始化的变量

局部初始化

局部未初始化

Static

.data

.bss

.data

.bss

Const

.rodata

编译错误必须初始化

.text

编译错误必须初始化

无修饰

.data

.bss

 

 

 



你可能感兴趣的:(C++)