18

编译时:

// 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 ,而在第二种情况下没有。
4

3 回答 3

11

一般来说,bss节包含未初始化的值,data节包含已初始化的值。但是,gcc 将初始化为零的值放入bss部分而不是data部分,因为bss无论如何该部分在运行时都被清零,在该部分中存储零没有多大意义data,这可以节省一些磁盘空间,来自 man海湾合作委员会:

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

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

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

于 2012-11-27T09:33:45.937 回答
5

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

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

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

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

在第二个示例中,因为您给出了一个非零初始化器,它知道它位于已初始化的数据段data中。 value1看起来很相似,但是对于value2,编译器需要为初始化器预留空间。在这种情况下,它不需要标记为,.local因为它可以只设置值并完成它。它不是全球性的,因为没有.globl针对它的声明。

顺便说一句,http: //refspecs.linuxbase.org/是访问有关二进制格式等的一些低级详细信息的好地方。

于 2012-11-27T09:55:25.753 回答
3

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

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

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

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

一个有趣的结果是,在 BSS 中初始化的数据大小不会包含在程序二进制文件中,而数据段中的数据大小会包含在内。

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

static int arr[1000] = {2};

在后一种情况下,可执行文件的大小将明显更大

于 2012-11-27T09:36:45.920 回答