C中的bss段
在关于 “ 关于Unix中的bss段和数据段 ”的问题的答案之一中,我看到对bss的解释如下:
Bss是特殊的:.bss对象不占用目标文件中的任何空间,并且通过将未特别初始化的所有符号分组在一起,可以很容易地将它们一次归零。
但是当我在目标文件上使用size时,生成的代码:
#include int uninit_global_var; int init_global_var=5; int main() { int local_var; return 0; }
我有以下内容
text data bss dec hex filename 1231 280 12 1523 5f3 a.out
并根据具有全局范围的未初始化数据成员查看bss的增长情况。 那么有人可以certificate上述陈述的合理性吗?
如果你删除stdio.h你的输出可能会更有意义。 让我们忽略该库,因为它包含内部变量。
在您的具体情况下,会发生以下情况:
int uninit_global_var;
因为这是在文件范围内分配的变量具有静态存储持续时间 ,就像任何声明为static
变量一样。 C标准要求如果程序员未明确初始化具有静态存储持续时间的变量,则在程序启动之前必须将其设置为零。 所有这些变量都放在.bss
段中。
int init_global_var=5;
此变量也在文件范围内分配,因此它也将具有静态存储持续时间。 但在这种情况下,它由程序员初始化。 C标准要求在程序启动之前将这些变量设置为给定的值。 这些变量放在.data
段中。
int local_var;
此变量具有自动存储持续时间(本地)。 编译器很可能会优化掉这个变量,因为它没有任何用途。 但我们假设这种优化不会发生。 然后,变量将在运行时分配,当它所在的范围(块)被执行时,一旦该范围被finsihed(它超出范围)就停止存在。 它将在堆栈或CPU寄存器中分配。 换句话说,在链接时,这个变量只作为程序代码存在,以某些汇编指令的forms表示“在堆栈上推送一个int”然后“从堆栈中弹出一个int”。
如何初始化这些不同类型的变量取决于系统。 但通常会在调用main之前由编译器注入一些代码。 这是一种过度简化,但出于教学法的考虑,您可以想象您的程序实际上是这样的:
bss { int uninit_global_var; } data { int init_global_var; } rodata { 5; } int start_of_program (void) // called by OS { memset(bss, 0, bss_size); memcpy(data, rodata, data_size); return main(); }
数据:4 bss:4
具有真正非易失性存储器的嵌入式系统将与上述代码完全相同,而基于RAM的系统可以不同地解决数据初始化部分。 bss在所有系统上都是一样的。
您可以通过运行以下程序轻松validation它们是否存储在不同的段中:
char uninit1; char uninit2; char init1 = 1; char init2 = 2; int main (void) { char local1 = 1; char local2 = 2; printf("bss\t%p\t%p\n", &uninit1, &uninit2); printf("data\t%p\t%p\n", &init1, &init2); printf("auto\t%p\t%p\n", &local1, &local2); }
您将看到“uninit”变量在相邻地址处分配,但在与其他变量不同的地址处分配。 与“init”变量相同。 “本地”变量可以在任何地方分配,因此您可以从这两个变量获得任何类型的奇怪地址。
我肯定不知道答案,但我的猜测是:
bss段的SIZE位于目标文件中,并按大小显示 – >毕竟必须分配它。
但是当bss段增长时,目标文件不会增长。
a.out
可能不是一个目标文件,它可能是一个ELF – 完整的可执行文件。 可重定位的对象(通常名为.o)是链接发生之前的中间文件。 请参阅gcc的-c选项。
bss段增长,但你的二进制文件中不需要这个段(参见objcopy)。
所以最终如果你把这些代码放到某种ROM中,它就不会占用空间,但需要RAM中的空间(以及将其初始化为0的代码)。