C中的bss段

p4rjhz4m  于 2023-08-03  发布在  其他
关注(0)|答案(4)|浏览(127)

在问题“Regarding the bss segment and data segment in Unix”的一个答案中,我看到了关于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在全局范围内基于未初始化的数据成员增长。那么,有人能证明上述说法吗?

htzpubme

htzpubme1#

如果你删除stdio.h,你的输出可能会更有意义。让我们忽略这个库,因为它包含内部变量。
在您的特定情况下,会发生以下情况:

int uninit_global_var;

字符串
因为这是一个在 file scope 中分配的变量,所以它具有 static storage duration,就像任何声明为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”变量相同。“局部”变量可以被分配到任何地方,所以你可以从这两个变量中得到任何奇怪的地址。

ctrmrzij

ctrmrzij2#

我不知道答案,但我的猜测是:
bss段的SIZE在目标文件中,并通过size ->显示,毕竟它必须被分配。
但是当bss段增长时,目标文件不会增长。

eblbsuwk

eblbsuwk3#

bss段会增长,但是你不需要在二进制文件中使用这个段(参见objcopy)。
因此,最终如果您要将此代码放入某种ROM中,则不会占用任何空间,但需要RAM中的空间(以及将其初始化为0的代码)。

kpbwa7wx

kpbwa7wx4#

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

相关问题