问题 静态局部变量在哪里


静态局部变量存储在内存中的哪个位置?只能在声明它们的函数内访问局部变量。

全局静态变量进入.data段。

如果静态全局变量和静态局部变量的名称相同,编译器如何区分它们?


5618
2018-05-23 00:45


起源

为什么您认为编译器在链接器级别使用的名称与您声明的名称相同? - bmargulies
这个可能重复:[C程序中“静态”是什么意思] [1] [1]: stackoverflow.com/questions/572547/... - tejas
可能重复 存储静态变量的位置(在C / C ++中)? - jogojapan


答案:


静态变量与全局变量进入同一段。两者之间唯一不同的是编译器“隐藏”链接器中的所有静态变量:只有外部(全局)变量的名称才会被暴露。这就是编译器如何允许具有相同名称的静态变量存在于不同的转换单元中。静态变量的名称在编译阶段仍然是已知的,但随后它们的数据被放入 .data 匿名细分。


11
2018-05-23 00:48





静态变量几乎与全局变量相似,因此未初始化的静态变量在BSS中,初始化的静态变量在数据段中。


2
2018-05-23 03:47





正如dasblinken所提到的,GCC 4.8将局部静态与全局变量放在同一个地方。

更确切地说:

  • static int i = 0 继续 .bss
  • static int i = 1 继续 .data

让我们分析一个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 -S main.o

f 包含:

int f() {
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
    static int i = 1;
    i++;
   4:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
   a:   83 c0 01                add    $0x1,%eax
   d:   89 05 00 00 00 00       mov    %eax,0x0(%rip)        # 13 <f+0x13>
    return i;
  13:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 19 <f+0x19>
}
  19:   5d                      pop    %rbp
  1a:   c3                      retq   

哪3次访问 i

  • 4 搬到了 eax 准备增量
  • d 将递增的值移回内存
  • 13 移动 i 到了 eax 为了返回值。从那以后显然是不必要的 eax 已经包含它,和 -O3 能够删除。

所以我们只关注它 4

4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>

我们来看看重定位数据:

readelf -r main.o

其中说明了链接器在创建可执行文件时如何修改文本部分地址。

它包含:

Relocation section '.rela.text' at offset 0x660 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000006  000300000002 R_X86_64_PC32     0000000000000000 .data - 4

我们看看 .rela.text 而不是其他人因为我们对重新安置感兴趣 .text

Offset 6 直接落入从字节4开始的指令:

4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
          ^^
          This is offset 6

根据我们对x86-64指令编码的了解:

  • 8b 05 是个 mov 部分
  • 00 00 00 00 是地址部分,从字节开始 6

AMD64 System V ABI更新 告诉我们 R_X86_64_PC32 作用于4个字节(00 00 00 00)并将地址计算为:

S + A - P

意思是:

  • S:段指出: .data
  • AAdded-4
  • P:加载时字节6的地址

-P 是需要的,因为GCC使用 RIP 相对地址,所以我们必须打折 .text

-4 需要因为 RIP 指向byte处的以下指令 0xA 但 P 是字节 0x6,所以我们需要打折4。

结论:链接后它将指向的第一个字节 .data 分割。


0
2018-05-28 19:53