8

在“关于 Unix 中的 bss 段和数据段”问题的答案之一中,我看到了关于 bss 的解释如下:

Bss 很特别:.bss 对象在对象文件中不占用任何空间,并且通过将所有未专门初始化的符号组合在一起,可以轻松地将它们一次清零。

但是当我在目标文件上使用 size 时,从代码中生成:

#include <stdio.h>
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 增长。那么任何人都可以证明上述声明的合理性吗?

4

4 回答 4

9

如果您删除 stdio.h 您的输出可能会更有意义。让我们忽略那个库,因为它包含内部变量。

在您的具体情况下,会发生以下情况:

int uninit_global_var;

由于这是在文件范围内分配的变量,因此具有静态存储持续时间,就像任何声明为static. C 标准要求,如果具有静态存储持续时间的变量没有被程序员显式初始化,在这种情况下,必须在程序启动之前将其设置为零。所有这些变量都放在.bss段中。

int init_global_var=5;

此变量也在文件范围内分配,因此它也将具有静态存储持续时间。但在这种情况下,它是由程序员初始化的。C 标准要求在程序启动之前将这些变量设置为给定的值。这些变量被放置在.data段中。

   int local_var;

此变量具有自动存储持续时间(本地)。编译器很可能会优化掉这个变量,因为它没有任何意义。但是让我们假设这种优化不会发生。然后,该变量将在运行时分配,当它所在的范围(块)被执行时,一旦该范围结束(它超出范围)就不再存在。它将被分配在堆栈上或 CPU 寄存器中。换句话说,在链接时,这个变量只作为程序代码存在,以一些汇编指令的形式存在,说“将一个 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 在所有系统上的工作方式都相同。


您可以通过运行以下程序轻松验证它们是否存储在不同的段中:

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”变量相同。“本地”变量可以分配到任何地方,因此您可以从这两个变量中获得任何类型的奇怪地址。

于 2012-10-09T11:48:02.800 回答
6

我不确定答案,但我有根据的猜测是:

bss 段的大小在目标文件中,并以大小显示-> 毕竟必须分配它。

但是当 bss 段增长时,目标文件不会增长。

于 2012-10-09T10:57:31.903 回答
2

a.out可能不是目标文件,它可能是 ELF - 完整的可执行文件。可重定位对象,通常命名为name .o,是链接发生之​​前的中间文件。请参阅 gcc 的 -c 选项。

于 2012-10-09T10:58:04.260 回答
2

bss 段会增长,但您的二进制文件中不需要此段(请参阅 objcopy)。

因此,最终如果您将此代码放入某种 ROM 中,它不会占用任何空间,但需要 RAM 中的空间(以及将其初始化为 0 的代码)。

于 2012-10-09T11:56:33.707 回答