Ben*_*oit 169 c c++ compiler-construction
在可执行文件的哪个段(.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);
} }
Run Code Online (Sandbox Code Playgroud)
如果我编译两个文件并将其链接到重复调用fooTest()和barTest的main,则printf语句将独立增加.有意义,因为foo和bar变量是翻译单元的本地变量.
但是存储分配在哪里?
需要明确的是,假设您有一个工具链可以输出ELF格式的文件.因此,我相信,有有将一些空间,对于那些静态变量的可执行文件保留.
出于讨论目的,我们假设我们使用GCC工具链.
Don*_*eld 124
你的静力学去哪里取决于它们是否为零初始化.零初始化静态数据进入.BSS(由符号开始的块),非零初始化数据进入.DATA
kar*_*arn 109
当程序加载到内存中时,它被组织成不同的段.其中一个细分是DATA细分.数据段进一步细分为两部分:
初始化数据段:所有全局,静态和常量数据都存储在此处.
未初始化的数据段(BSS):所有未初始化的数据都存储在该段中.
这是一个解释这个概念的图表:

这里是解释这些概念的非常好的链接:
小智 32
实际上,变量是元组(存储,范围,类型,地址,值):
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
Run Code Online (Sandbox Code Playgroud)
本地范围可能意味着转换单元(源文件),函数或块的本地,取决于其定义的位置.要使变量对多个函数可见,它必须位于DATA或BSS区域(取决于它是否分别显式初始化).然后根据源文件中的所有函数或函数确定其范围.
Seb*_*ose 21
数据的存储位置将取决于实现.
但是,静态的含义是"内部联系".因此,符号在编译单元内部(foo.c,bar.c),不能在编译单元外引用.所以,没有名称冲突.
pax*_*blo 13
我不相信会有碰撞.在文件级别使用static(外部函数)将变量标记为当前编译单元(文件)的本地变量.它在当前文件之外永远不可见,因此永远不必拥有名称.
在函数内部使用static是不同的 - 变量只对函数可见,它的值只是在对该函数的调用中保留.
实际上,静态根据它的位置做两件事.但是,在其他情况下,它会限制变量的可见性以防止命名空间冲突,
话虽如此,我相信它会存储在DATA中,而DATA往往具有初始化变量.BSS最初代表byte-set- <something>,它包含未初始化的变量.
Cir*_*四事件 10
如何自己找到它 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;
}
Run Code Online (Sandbox Code Playgroud)
编译:
gcc -ggdb -c main.c
Run Code Online (Sandbox Code Playgroud)
用以下代码反编译代码:
objdump -Sr main.o
Run Code Online (Sandbox Code Playgroud)
-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
Run Code Online (Sandbox Code Playgroud)
并.data-0x4说它将转到该.data段的第一个字节.
那-0x4是因为我们正在使用RIP相对寻址,因此%rip在指令和中R_X86_64_PC32.
这是必需的,因为RIP指向以下指令,该指令从4个字节开始,之后00 00 00 00将重新定位.我在以下网址详细解释了这一点:https://stackoverflow.com/a/30515926/895245
然后,如果我们修改源i = 1并进行相同的分析,我们得出结论:
static int i = 0 继续 .bssstatic int i = 1 继续 .data在编译单元中声明的数据将进入.BSS或该文件输出的.Data.BSS中的初始化数据,未在DATA中初始化.
静态数据和全局数据之间的区别在于在文件中包含符号信息.编译器倾向于包括符号信息,但仅标记全局信息.
链接器尊重此信息.静态变量的符号信息被丢弃或损坏,因此仍可以某种方式引用静态变量(使用调试或符号选项).在任何情况下,编译器单元都不会受到影响,因为链接器首先解析本地引用.