可执行文件的哪个段(.bss,.data,other)中存储了静态变量,以便它们不会发生名称冲突?例如:
1 2 3 4 5 6 7 8
| 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);
} } |
如果我编译这两个文件并将其链接到一个重复调用footerst()和bartest的main,printf语句将独立递增。有意义,因为foo和bar变量是翻译单元的本地变量。
但是存储在哪里分配呢?
要清楚地说,假设您有一个工具链,可以以elf格式输出文件。因此,我认为在可执行文件中必须为这些静态变量保留一些空间。出于讨论目的,假设我们使用gcc工具链。
静态的位置取决于它们是否初始化为零。零初始化静态数据进入.bss(以符号开头的块),非零初始化数据进入.data
当一个程序装入内存时,它被组织成不同的段。其中一个段是数据段。数据段进一步分为两部分:初始化数据段:所有的全局、静态和常量数据都存储在这里。未初始化数据段(BSS):所有未初始化的数据都存储在此段中。
下面是一个图表来解释这个概念:

以下是解释这些概念的非常好的链接:
http://www.inf.udec.cl/~leo/teoX.pdf
实际上,变量是元组(存储、作用域、类型、地址、值):
1 2 3 4 5
| storage : where is it stored, for example data, stack, heap...
scope : who can see us, for example global, local...
type : what is our type, for example int, int*...
address : where are we located
value : what is our value |
局部作用域可以是局部到翻译单元(源文件)、函数或块,这取决于其定义的位置。要使变量对多个函数可见,它必须位于数据或BSS区域中(取决于其是否显式初始化)。然后它的作用域相应地限定为源文件中的所有函数或函数。
数据的存储位置取决于实现。
然而,静态的含义是"内部联系"。因此,符号是编译单元(foo.c,bar.c)的内部符号,不能在编译单元之外引用。所以,不会有名称冲突。
我不相信会有碰撞。在文件级使用static(外部函数)将变量标记为当前编译单元(文件)的本地变量。它在当前文件之外永远不可见,因此不必有名称。
在函数内部使用static是不同的-变量只对函数可见,它的值只在调用该函数时保留。
实际上,static根据其所在位置执行两种不同的操作。但是,在其他情况下,它会限制变量的可见性,以防止名称空间冲突,
尽管如此,我相信它将存储在倾向于初始化变量的数据中。BSS最初代表字节集-,它保存未初始化的变量。
如何与EDOCX1[0]一起找到它
要真正了解正在发生的事情,您必须了解链接器重新定位。如果你从未接触过这一点,请考虑先阅读这篇文章。
让我们分析一个Linux x86-64 ELF示例来了解它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d
", f());
printf("%d
", f());
return 0;
} |
编译:
用以下代码解压代码:
- -S将代码分解为与原始源混合的代码。
- -r显示搬迁信息
在f的反编译过程中,我们看到:
1 2 3 4
| 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段的第一个字节。
因为我们使用的是RIP相对寻址,所以存在-0x4,因此指令中的%rip和R_X86_64_PC32。
这是必需的,因为rip指向以下指令,该指令在00 00 00 00之后开始4个字节,这将被重新定位。我在https://stackoverflow.com/a/30515926/895245中对此进行了更详细的解释。
然后,如果我们将源代码修改为i = 1,并进行相同的分析,我们得出结论:
- static int i = 0接.bss。
- static int i = 1接.data。
在"全局和静态"区域中:)
C++中有几个内存区域
请参阅此处了解您的问题的详细答案
这取决于您使用的平台和编译器。一些编译器直接存储在代码段中。静态变量总是只能被当前翻译单元访问,并且不会导出名称,因此不会发生名称冲突。
编译单元中声明的数据将进入该文件输出的.bss或.data。已在BSS中初始化数据,未在数据中倾斜。
静态数据和全局数据之间的区别在于在文件中包含符号信息。编译器倾向于包含符号信息,但只标记全局信息。
链接器尊重此信息。静态变量的符号信息被丢弃或损坏,这样静态变量仍可以以某种方式引用(使用调试或符号选项)。在任何情况下,当链接器首先解析本地引用时,编译单元都不会受到影响。
这是如何(容易理解):

这个问题有点太老了,但由于没有人指出任何有用的信息:通过"mohit12379"查看帖子,解释符号表中同名静态变量的存储:http://www.geekinterview.com/questionu详细信息/24745
我用objdump和gdb尝试过,结果是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| (gdb) disas fooTest
Dump of assembler code for function fooTest:
0x000000000040052d <+0>: push %rbp
0x000000000040052e <+1>: mov %rsp,%rbp
0x0000000000400531 <+4>: mov 0x200b09(%rip),%eax # 0x601040 <foo>
0x0000000000400537 <+10>: add $0x1,%eax
0x000000000040053a <+13>: mov %eax,0x200b00(%rip) # 0x601040 <foo>
0x0000000000400540 <+19>: mov 0x200afe(%rip),%eax # 0x601044 <bar.2180>
0x0000000000400546 <+25>: add $0x1,%eax
0x0000000000400549 <+28>: mov %eax,0x200af5(%rip) # 0x601044 <bar.2180>
0x000000000040054f <+34>: mov 0x200aef(%rip),%edx # 0x601044 <bar.2180>
0x0000000000400555 <+40>: mov 0x200ae5(%rip),%eax # 0x601040 <foo>
0x000000000040055b <+46>: mov %eax,%esi
0x000000000040055d <+48>: mov $0x400654,%edi
0x0000000000400562 <+53>: mov $0x0,%eax
0x0000000000400567 <+58>: callq 0x400410 <printf@plt>
0x000000000040056c <+63>: pop %rbp
0x000000000040056d <+64>: retq
End of assembler dump.
(gdb) disas barTest
Dump of assembler code for function barTest:
0x000000000040056e <+0>: push %rbp
0x000000000040056f <+1>: mov %rsp,%rbp
0x0000000000400572 <+4>: mov 0x200ad0(%rip),%eax # 0x601048 <foo>
0x0000000000400578 <+10>: add $0x1,%eax
0x000000000040057b <+13>: mov %eax,0x200ac7(%rip) # 0x601048 <foo>
0x0000000000400581 <+19>: mov 0x200ac5(%rip),%eax # 0x60104c <bar.2180>
0x0000000000400587 <+25>: add $0x1,%eax
0x000000000040058a <+28>: mov %eax,0x200abc(%rip) # 0x60104c <bar.2180>
0x0000000000400590 <+34>: mov 0x200ab6(%rip),%edx # 0x60104c <bar.2180>
0x0000000000400596 <+40>: mov 0x200aac(%rip),%eax # 0x601048 <foo>
0x000000000040059c <+46>: mov %eax,%esi
0x000000000040059e <+48>: mov $0x40065c,%edi
0x00000000004005a3 <+53>: mov $0x0,%eax
0x00000000004005a8 <+58>: callq 0x400410 <printf@plt>
0x00000000004005ad <+63>: pop %rbp
0x00000000004005ae <+64>: retq
End of assembler dump. |
这是objdump结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Disassembly of section .data:
0000000000601030 <__data_start>:
...
0000000000601038 <__dso_handle>:
...
0000000000601040 <foo>:
601040: 01 00 add %eax,(%rax)
...
0000000000601044 <bar.2180>:
601044: 02 00 add (%rax),%al
...
0000000000601048 <foo>:
601048: 0a 00 or (%rax),%al
...
000000000060104c <bar.2180>:
60104c: 14 00 adc $0x0,%al |
所以,也就是说,四个变量位于数据节事件中,名称相同,但偏移量不同。
静态变量存储在前面提到的数据段或代码段中。您可以确保不会在堆栈或堆上分配它。由于static关键字将变量的作用域定义为文件或函数,因此没有发生冲突的风险,如果发生冲突,则会有编译器/链接器警告您。一个很好的例子
答案很可能取决于编译器,所以你可能想编辑你的问题(我的意思是,即使段的概念不是由ISOC和ISO C++授权的)。例如,在Windows上,可执行文件不带符号名。一个"foo"是偏移量0x100,另一个可能是0x2b0,编译来自两个翻译单元的代码时都知道"它们的"foo的偏移量。
它们都将独立存储,但是如果您想向其他开发人员清楚地表明这一点,您可能会希望将它们包装在名称空间中。
您已经知道它存储在BSS(以符号开头的块)中,也称为未初始化的数据段,或者存储在已初始化的数据段中。
让我们举个简单的例子
1 2 3 4
| void main(void)
{
static int i;
} |
上述静态变量未初始化,因此将转到未初始化的数据段(BSS)。
1 2 3 4
| void main(void)
{
static int i=10;
} |
当然,它是由10初始化的,所以它进入初始化的数据段。