为什么 GCC 以不同方式存储全局和静态 int?

Why does GCC store global and static int differently?

这是我的 C 程序,其中包含一个静态变量、两个全局变量、一个局部变量和一个外部变量。

#include <stdio.h>

int gvar1;
int gvar2 = 12;
extern int evar = 1;
int main(void)
{
    int lvar;
    static int svar = 4;
    lvar = 2;
    gvar1 = 3;
    printf ("global1-%d global2-%d local+1-%d static-%d extern-%d\n", gvar1, gvar2, (lvar+1), svar, evar);
return 0;
}

请注意,gvar1、gvar2、evar、lvar 和 svar 都定义为整数。

我使用 objdump 反汇编了代码,debug_str 显示如下:

Contents of section .debug_str:
 0000 76617269 61626c65 732e6300 6c6f6e67  variables.c.long
 0010 20756e73 69676e65 6420696e 74002f75   unsigned int./u
 0020 73657273 2f686f6d 6534302f 72616f70  sers/home40/raop
 0030 2f626b75 702f6578 616d706c 65730075  /bkup/examples.u
 0040 6e736967 6e656420 63686172 00737661  nsigned char.sva
 0050 72006d61 696e006c 6f6e6720 696e7400  r.main.long int.
 0060 6c766172 0073686f 72742075 6e736967  lvar.short unsig
 0070 6e656420 696e7400 67766172 31006776  ned int.gvar1.gv
 0080 61723200 65766172 00474e55 20432034  ar2.evar.GNU C 4
 0090 2e342e36 20323031 31303733 31202852  .4.6 20110731 (R
 00a0 65642048 61742034 2e342e36 2d332900  ed Hat 4.4.6-3).
 00b0 73686f72 7420696e 7400               short int.

为什么会显示以下内容?

unsigned char.svar
long int.lvar
short unsigned int.gvar1.gvar2.evar

GCC 如何决定应将其存储为哪种类型?

我正在使用 GCC 4.4.6 20110731 (Red Hat 4.4.6-3)

C11 specification (read n1570) - 或更早的 C 标准 - 没有定义存储全局或静态变量的地址或偏移量,因此实现(您的 gcc 编译器和您的 ld链接器)可以随意将它们放在任何地方。

data segments 的组织和布局是一个实现细节。

您可能需要阅读更多关于 DWARF 的内容以了解调试信息,这对 gdb 调试器很有用。

您可能想阅读有关 linkers and loaders, and about the ELF format, if you want to understand how they are working. On Linux, there are several utilities to inspect elf(5) files, including objdump(1), readelf(1), nm(1) 的更多信息。

请注意,您的 GCC4.4 是已过时的旧版本 GCC。当前版本是 GCC7,GCC8 将在几周后发布(spring 2018)。我强烈建议升级你的编译器。

如果您需要了解数据段的组织方式和原因,以及您的实施选择这种布局的原因,您可以利用 gccld(来自 binutils) are free software,并详细研究它们的源代码。你需要多年的工作,因为它们是复杂的软件(超过一千万行源代码)。

如果你刚好开始研究GCC, be sure to study a recent version. Most people of the GCC community have probably forgotten the details of GCC4.4 (released in 2009). A lot of things have changed in GCC since that ancient thing. A few years ago, I have written many slides about GCC internals, see the documentation of GCC MELT的内部结构。

顺便说一句,数据段或其中变量的布局可能会因 optimization options. It might happen that lvar does not sit in memory (e.g. stays in a register only); it could happen that a static variable is removed (using something like the as-if rule) 等而异

对于单个translation unitfoo.c,您可以使用gcc -fverbose-asm -S -O foo.c将其编译成汇编代码并查看发出的foo.s 汇编代码。

要了解更多 ld 链接器的工作原理,您可以查看一些相关的 linker script。您可以找到如何通过在编译和链接命令中使用 gcc -v(而不是 gcc)从 gcc 调用 ld

在大多数情况下,你不应该关心特定的偏移量(在object files or executables) or addresses (in the virtual address space of your process) of global or static variables. Be also aware of ASLR. The proc(5)文件系统中可以用来了解你的过程。

(你的问题严重缺乏动机和背景)

Why is it showing the following?

简单回答:它没有显示您的想法,而是显示:

1 "variables.c"
2 "long unsigned int"
2a "unsigned int"
2b "int"
3 "/users/home40/raop/bkup/examples"
4 "unsigned char"
4a "char"
5 "svar"
6 "main"
7 "long int"
8 "lvar"
9 "short unsigned int"
10 "gvar1"
11 "gvar2"
12 "evar"
13 "GNU C 4.4.6 20110731 (Red Hat 4.4.6-3)"
14 "short int"

该部分名为 .debug_str;它包含一个由 NUL 字节分隔的字符串列表。这些字符串 以任意顺序 并且它们被 .debug_info 部分引用。所以 svar 跟随 unsigned char 这一事实 根本没有任何意义

.debug_info 部分包含实际的调试信息。此部分不包含字符串。相反,它将包含如下信息:

    ...
Item 123:
    Type of information: Data type
    Name: 2b /* String #2b in ".debug_str" is "int" */
    Kind of data type: Signed integer
    Number of bits: 32
    ... some more information ...
Item 124:
    Type of information: Global variable
    Name: 8 /* "lvar" */
    Data type defined by: Item 123
    Stored at: Address 0x1234
    ... some more information ...
Begin item 125:
    Type of information: Function
    Name: 6 /* "main" */
    ... some more information ...
Item 126:
    Type of information: Local variable
    Name: 5 /* "svar" */
    Data type defined by: Item 123
    Stored at: Address 0x1238
    ... some more information ...
End item 125 /* Function "main" */
Item 127:
    ...

您可以使用以下命令查看此信息:

readelf --debug-dump filename.o

Why does GCC store global and static int differently?

我编译了你的例子两次:一次有优化,一次没有优化。

没有优化 svargvar1 的存储方式 完全相同 :数据类型 int,存储在固定地址。 lvar 是:数据类型 int,存储在堆栈中。

经过优化 lvarsvar 以相同的方式存储:数据类型:int,根本不存储,而是将它们视为常量值。

(这是有道理的,因为这些变量的值永远不会改变。)