为什么在BSS段和数据段中分配给整数的内存大小不同?

4
请查看以下程序 -
#include <stdio.h>  
void main()
{
}

每个段分配的内存如下(使用Unix上的size命令)-
   text    data     bss     dec     hex filename
   1040     484      16    1540     604 try

在全局变量声明后 -
#include <stdio.h>
int i;

void main()
{
}

每个段分配的内存如下(在Unix上使用size命令)。这里变量'i'在BSS中获得了内存(之前是16,现在是24)。

   text    data     bss     dec     hex filename
   1040     484      24    1548     60c try

在声明全局变量并将其初始化为10之后-
#include <stdio.h>
int i=10;

void main()
{
}

每个段分配的内存如下(通过在Unix上使用size命令)
在数据段中变量'i'已经接收到了内存(以前是484,现在是488)-

   text    data     bss     dec     hex filename
   1040     488      16    1544     608 try

我的问题是,为什么全局变量“i”在存储在BSS段时占用了8个字节的内存,但在存储在数据段时只占用了4个字节的内存?在BSS段和数据段中为整数分配内存的差异在哪里?

可能是对齐要求的问题。 - Stargateur
在我的机器上(x86_64 Linux),.bss大小似乎会向下舍入为8的倍数,而.data部分则不会。换句话说,我需要添加两个未初始化的整数才能看到增加8,但每个初始化的整数都会使.data部分增加4。这是一个有趣的问题。 - Petr Skocik
未初始化的变量放在bss部分,这应该是一个虚拟部分,仅保存要保留的空间计数器,但根据二进制格式请求对齐。当声明已初始化的变量时,它的初始值存储在数据部分中,使其增加4个字节以适应32位int。这最终会改变文件映像中后面的bss部分的位置。如果将对齐间距计算为bss空间,则该值将更改。 - Frankie_C
由于这一切高度依赖编译器,因此您应该在编译器标志旁注明使用的编译器和版本。 - Serge Ballesta
@Frankie_C 谢谢你的信息 :) - Stargateur
显示剩余3条评论
1个回答

2
为什么全局变量'i'在存储在BSS段时占用8个字节的内存,而在数据段中占用4个字节的内存?
首先,为什么数据段中是4个字节?
正如许多人已经回答的那样 - .data段包含任何预先初始化的全局或静态变量。整数大小为4个字节,在你的程序中有全局int i=10;时,这反映在数据段大小上。
现在,为什么.bss段中是8个字节?
你观察到这种行为是因为GNU链接器GNU ld的默认链接脚本。你可以在这里获取有关链接脚本的信息。
在链接时,GNU链接器(GNU ld)使用默认链接脚本。
默认链接脚本指定了.bss段的对齐方式。
如果您想查看默认的链接器脚本,可以使用以下命令进行查看:
gcc -Wl,-verbose main.c

这个 gcc 命令的输出将包含以下语句:
using internal linker script:
==================================================
// The content between these two lines is the default linker script
==================================================

在默认的链接脚本中,您可以找到.bss部分:
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }

在这里,你可以看到. = ALIGN(. != 0 ? 64 / 8 : 1);,它表示默认对齐方式为8个字节。
程序:
#include <stdio.h>
int i;

void main()
{
}

当使用默认的链接器脚本构建时,“i”会在BSS中获得8字节大小的内存,因为需要8字节对齐。
# size a.out
   text    data     bss     dec     hex filename
   1040     484      24    1548     60c a.out
24字节(16 + 8) GNU链接器提供了一种方法,可以将自己的链接脚本传递给它,在这种情况下,它使用传递给它的脚本来构建目标,而不是默认的链接脚本。
只需尝试此操作,您可以将默认链接脚本的内容复制到文件中,并使用此命令将您的链接脚本传递给GNU ld:
gcc -Xlinker -T my_linker_script main.c

由于您可以拥有自己的链接脚本,因此您可以对其进行更改并查看行为的变化。
.bss部分中,将. = ALIGN(. != 0 ? 64 / 8 : 1);更改为. = ALIGN(. != 0 ? 32 / 8 : 1);。这将把默认对齐方式从8字节改为4字节。现在使用具有此更改的链接脚本构建您的目标。
输出是:
# size a.out
   text    data     bss     dec     hex filename
   1040     484      20    1544     608 a.out

在这里,您可以看到bss的大小为20字节(16 + 4),因为需要进行4字节对齐。
希望这回答了您的问题。

绝对正确... - Frankie_C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接