在可执行文件的哪个段(.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的main,则printf语句将独立增加.有意义,因为foo和bar变量是翻译单元的本地变量.
但是存储分配在哪里?
需要明确的是,假设您有一个工具链可以输出ELF格式的文件.因此,我相信,有有将一些空间,对于那些静态变量的可执行文件保留.
出于讨论目的,我们假设我们使用GCC工具链.
你的静力学去哪里取决于它们是否为零初始化.零初始化静态数据进入.BSS(由符号开始的块),非零初始化数据进入.DATA
当程序加载到内存中时,它被组织成不同的段.其中一个细分是DATA细分.数据段进一步细分为两部分:
初始化数据段:所有全局,静态和常量数据都存储在此处.
未初始化的数据段(BSS):所有未初始化的数据都存储在该段中.
这是一个解释这个概念的图表:
这里是解释这些概念的非常好的链接:
http://www.inf.udec.cl/~leo/teoX.pdf
实际上,变量是元组(存储,范围,类型,地址,值):
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
本地范围可能意味着转换单元(源文件),函数或块的本地,取决于其定义的位置.要使变量对多个函数可见,它必须位于DATA或BSS区域(取决于它是否分别显式初始化).然后根据源文件中的所有函数或函数确定其范围.
数据的存储位置将取决于实现.
但是,静态的含义是"内部联系".因此,符号在编译单元内部(foo.c,bar.c),不能在编译单元外引用.所以,没有名称冲突.
我不相信会有碰撞.在文件级别使用static(外部函数)将变量标记为当前编译单元(文件)的本地变量.它在当前文件之外永远不可见,因此永远不必拥有名称.
在函数内部使用static是不同的 - 变量只对函数可见,它的值只是在对该函数的调用中保留.
实际上,静态根据它的位置做两件事.但是,在其他情况下,它会限制变量的可见性以防止命名空间冲突,
话虽如此,我相信它会存储在DATA中,而DATA往往具有初始化变量.BSS最初代表byte-set-
在"全球和静态"领域:)
C++中有几个内存区域
堆
免费商店
堆
全球和静态
常量
请看这里详细解答您的问题
如何自己找到它 objdump -Sr
要真正了解发生了什么,您必须了解链接器重定位.如果你从未接触到这一点,请考虑先阅读这篇文章.
让我们自己分析一下Linux x86-64 ELF示例:
#includeint 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 # a6: R_X86_64_PC32 .data-0x4
并.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
继续 .bss
static int i = 1
继续 .data
这取决于您正在使用的平台和编译器.一些编译器直接存储在代码段中.静态变量始终只能由当前转换单元访问,并且不会导出名称,因此不会发生名称冲突的原因.
在编译单元中声明的数据将进入.BSS或该文件输出的.Data.BSS中的初始化数据,未在DATA中初始化.
静态数据和全局数据之间的区别在于在文件中包含符号信息.编译器倾向于包括符号信息,但仅标记全局信息.
链接器尊重此信息.静态变量的符号信息被丢弃或损坏,因此仍可以某种方式引用静态变量(使用调试或符号选项).在任何情况下,编译器单元都不会受到影响,因为链接器首先解析本地引用.