C中的局部和静态变量

编译时:

// external definitions int value1 = 0; static int value2 = 0; 

gcc编译器生成以下程序集:

 .globl value1 .bss .align 4 .type value1, @object .size value1, 4 value1: .zero 4 .local value2 .comm value2,4,4 

但是,当我将变量初始化为零以外的值时,例如:

 // external definitions int value1 = 1; static int value2 = 1; 

gcc编译器生成以下内容:

 .globl value1 .data .align 4 .type value1, @object .size value1, 4 value1: .long 1 .align 4 .type value2, @object .size value2, 4 value2: .long 1 

我的问题是:

  1. 为什么在第一种情况下,值在bss段中分配,而在第二种情况下在数据段中。
  2. 为什么value2变量在第一种情况下定义为.local和.comm,而在第二种情况下不定义。

一般来说, bss部分包含未初始化的值, data部分包含初始化值。 但是,gcc将初始化为零的值放入bss部分而不是data部分,因为无论如何bss部分在运行时被清零,在data部分存储零没有多大意义,这节省了一些磁盘空间,来自男人gcc:

-fno-zero-initialized-in-bss如果目标支持BSS部分, GCC默认将初始化为零的变量放入BSS。 这可以节省生成的代码中的空间。 此选项会关闭此行为,因为某些程序明确依赖于转到数据部分的变量

我不确定为什么.comm用于对象文件本地的静态存储,它通常用于声明公共符号,如果没有定义/初始化,应该由链接器与具有相同名称的符号合并来自其他目标文件,这就是为什么它没有在第二个例子中使用,因为变量是从as 手册初始化的

.comm声明一个名为symbol的通用符号。 链接时,一个目标文件中的公共符号可以与另一个目标文件中的相同名称的已定义或公共符号合并

第一种情况是因为您将值初始化为零。 它是C标准 (第6.7.8节)的一部分,如果没有指定,则全局整数初始化为0。 所以文件格式提供了一个规定,通过将特殊部分置于以下位置来保持二进制文件更小: bss 。 如果你看一些ELF规范 (第I-15页),你会发现:

.bss此部分包含有助于程序内存映像的未初始化数据。 根据定义,当程序开始运行时,系统用零初始化数据。 该部分不占用文件空间,如节类型SHT_NOBITS所示。

在第一种情况下,编译器进行了优化。 它不需要在实际的二进制文件中占用空间来存储初始化程序,因为它可以使用bss段并免费获得你想要的那个。

现在,你有一个静态来自外部源的事实有点有趣(通常不会这样做)。 在编译的模块中,不应该与其他模块共享,并且应该用.local标记。 我怀疑它是这样做的,因为没有为初始化程序存储的实际值。

在第二个示例中,因为您已经给出了非零初始化程序,所以它知道驻留在初始化的数据段datavalue1看起来非常相似,但对于value2 ,编译器需要为初始化程序保留空间。 在这种情况下,它不需要标记为.local因为它可以只放下值并完成它。 它不是全局的,因为它没有.globl语句。

BTW, http: //refspecs.linuxbase.org/是一个访问有关二进制格式等的一些低级细节的好地方。

BSS是包含在运行时初始化的数据的段,其中数据段包含在程序二进制文件中初始化的数据。

现在静态变量总是被初始化,无论是否在程序中明确地完成。 但是有两个单独的类别,初始化(DS)和未初始化(BSS)静态。

BSS中存在的所有值都是未在程序代码中初始化的值,因此在程序在运行时加载到0(如果是整数)时为初始化,对于指针等为null。

因此,当您使用0初始化时,该值将转到BSS,其中分配的任何其他值将在数据段中分配变量。

一个有趣的结果是, 在BSS中初始化的数据的大小将不包括在程序二进制中,其中包括数据段中的数据的大小。

尝试分配一个大的静态数组并在程序中使用它。 在代码中未明确初始化时,请查看可执行文件大小。 然后用非零值初始化它

 static int arr[1000] = {2}; 

后一种情况下可执行文件的大小将会大得多