在哪个区段?BSS, .DATA,其他)的可执行文件的静态变量存储,以便他们没有名称冲突? 例如:


foo.c:                         bar.c:
static int foo = 1;            static int foo = 10;
void fooTest() {               void barTest() {
  static int bar = 2;            static int bar = 20;
  foo++;                         foo++;
  bar++;                         bar++;
  printf("%d,%d", foo, bar);     printf("%d, %d", foo, bar);
}                              }

如果我编译这两个文件并将其链接到重复调用fooTest()和barTest的主程序,printf语句将独立递增。这是有意义的,因为foo和bar变量是翻译单元的局部变量。

但是存储分配在哪里呢?

需要明确的是,假设您有一个工具链,该工具链将以ELF格式输出文件。因此,我认为在可执行文件中必须为那些静态变量保留一些空间。 为了便于讨论,让我们假设我们使用GCC工具链。


当前回答

如何用objdump -Sr自己找到它

要真正理解发生了什么,您必须理解连接器重定位。如果你从未接触过,考虑先阅读这篇文章。

让我们来分析一个Linux x86-64 ELF的例子,看看它自己:

#include <stdio.h>

int f() {
    static int i = 1;
    i++;
    return i;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", f());
    return 0;
}

编译:

gcc -ggdb -c main.c

用以下方法反编译代码:

objdump -Sr main.o

-S混合原始源代码反编译代码 -r显示重定位信息

在f的反编译中,我们看到:

 static int i = 1;
 i++;
4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
        6: R_X86_64_PC32    .data-0x4

而.data-0x4表示它将到。data段的第一个字节。

-0x4的存在是因为我们正在使用RIP相对寻址,因此指令中的% RIP和R_X86_64_PC32。

它是必需的,因为RIP指向下面的指令,它在00 00 00 00 00之后4个字节开始,00 00 00 00将被重新定位。我已经在https://stackoverflow.com/a/30515926/895245上详细解释了这一点

然后,如果我们将源修改为i = 1,并进行同样的分析,我们得出:

静态int I = 0是。bss 静态int I = 1在。data上

其他回答

我相信不会发生碰撞。在文件级(外部函数)使用static将变量标记为当前编译单元(文件)的本地变量。它在当前文件之外永远不可见,因此永远不需要有一个可以在外部使用的名称。

在函数内部使用static则不同——变量只对函数可见(无论是否是静态的),只是它的值在调用该函数时被保留。

实际上,静态的作用取决于它所处的位置。但是,在这两种情况下,变量的可见性都受到限制,可以在链接时轻松防止名称空间冲突。

话虽如此,我相信它将存储在DATA部分中,该部分往往具有初始化为非零值的变量。当然,这是一个实现细节,而不是标准所强制要求的东西——它只关心行为,而不是事情在幕后是如何完成的。

这是如何(容易理解):

如前所述,存储在数据段或代码段中的静态变量。 您可以确保它不会被分配到堆栈或堆上。 没有碰撞风险,因为static关键字定义变量的范围为文件或函数,如果发生碰撞,有编译器/链接器警告你。

你已经知道要么它存储在bss(块开始的符号),也称为未初始化的数据段或初始化的数据段。

让我们举一个简单的例子

void main(void)
{
static int i;
}

上面的静态变量没有初始化,所以它进入未初始化的数据段(bss)。

void main(void)
{
static int i=10;
}

当然它初始化了10,所以它去初始化数据段。

这取决于你使用的平台和编译器。有些编译器直接存储在代码段中。静态变量总是只能被当前的翻译单元访问,并且名称不会被导出,因此名称冲突永远不会发生。