编译时:
// 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
我的问题是:
答案 0 :(得分:11)
一般来说,bss
部分包含未初始化的值,data
部分包含初始化值。但是,gcc将初始化为零的值放入bss
部分而不是data
部分,因为bss
部分在运行时已归零,它没有多大意义要在data
部分存储零,这可以节省一些磁盘空间,来自man gcc:
-fno-zero-initialized-in-bss如果目标支持BSS部分, GCC默认将初始化为零的变量放入BSS。这个 可以节省生成代码中的空间。此选项会关闭此选项 行为,因为一些程序明确依赖变量 数据部分
我不确定为什么.comm
与对象文件本地的静态存储一起使用,它通常用于声明常见符号,如果不< / em>已定义/初始化,应该由链接器与其他目标文件中具有相同名称的符号合并,这就是为什么在第二个示例中没有使用它,因为变量是从as
{{3}初始化的}}
.comm声明一个名为symbol的常用符号。链接时,很常见 一个目标文件中的符号可以与已定义或共同的内容合并 另一个目标文件中同名的符号
答案 1 :(得分:5)
第一种情况是因为您将值初始化为零。它是C standard(第6.7.8节)的一部分,如果没有指定,则全局整数初始化为0。因此,文件格式通过将特殊部分置于:bss
中来提供更小的二进制文件。如果您查看一些ELF specification(第I-15页),您会发现:
.bss此部分包含有助于该程序的未初始化数据 记忆图像。根据定义,系统用零初始化数据 当程序开始运行时。该部分不占用任何文件空间 由部分类型SHT_NOBITS指示。
在第一种情况下,编译器进行了优化。它不需要在实际二进制文件中占用空间来存储初始化程序,因为它可以使用bss
段并获得您想要的免费段。
现在,你有一个来自外部源的静态的事实有点有趣(通常不会这样做)。在编译的模块中,不应与其他模块共享,并应标记为.local
。我怀疑它是这样做的,因为没有为初始化程序存储的实际值。
在第二个示例中,因为您已经给出了非零初始值设定项,所以它知道它位于初始化数据段data
中。 value1
看起来非常相似,但对于value2
,编译器需要为初始化程序保留空间。在这种情况下,它不需要标记为.local
,因为它可以放下值并完成它。它不是全局的,因为它没有.globl
语句。
BTW,http://refspecs.linuxbase.org/是访问二进制格式等一些低级细节的好地方。
答案 2 :(得分:3)
BSS是包含在运行时初始化的数据的段,其中数据段包含在程序二进制文件中初始化的数据。
现在,无论是否在程序中明确地完成,静态变量总是被初始化。但是有两个单独的类别,初始化(DS)和未初始化(BSS)静态。
BSS中存在的所有值都是未在程序代码中初始化的值,因此在程序在运行时加载到0(如果是整数)时为初始化,对于指针等为null。
因此,当您使用0初始化时,该值将转到BSS,其中分配的任何其他值将在数据段中分配变量。
一个有趣的结果是,在BSS中初始化的数据大小将不包含在程序二进制文件中,其中包含数据段中的数据大小。
尝试分配大型静态数组并在程序中使用它。在代码中未明确初始化时,请查看可执行文件大小。然后用非零值(如
)初始化它static int arr[1000] = {2};
后一种情况下可执行文件的大小会大得多