【问题标题】:Local and static variables in CC中的局部变量和静态变量
【发布时间】:2012-11-27 09:22:38
【问题描述】:

编译时:

// external definitions
int value1 = 0;
static int value2 = 0;

gcc 编译器生成以下程序集:

.globl value1
        .bss
        .align 4
        .type   value1, @object
        .size   value1, 4
value1:
        .zero   4
        .local  value2
        .comm   value2,4,4

但是,当我将变量初始化为非零值时,例如:

// external definitions
int value1 = 1;
static int value2 = 1;

gcc 编译器生成以下内容:

.globl value1
        .data
        .align 4
        .type   value1, @object
        .size   value1, 4
value1:
        .long   1
        .align 4
        .type   value2, @object
        .size   value2, 4
value2:
        .long   1

我的问题是:

  1. 为什么在第一种情况下,值分配在 bss 段中,而在第二种情况下,分配在数据段中。
  2. 为什么 value2 变量在第一种情况下被定义为 .local 和 .comm,而在第二种情况下却没有。

【问题讨论】:

  • 很难从您的 C 代码 sn-p 中分辨出来,但 value1 似乎是一个全局变量(而不是本地变量)。将在堆栈上分配一个局部变量。
  • 很难说。我会考虑将它放入.bss 一个错误中。虽然与标准的观点没有区别(未初始化的变量应该初始化为0),但使用0的初始化与未初始化是不同的。
  • @Codo 我的 sn-ps 中的两个变量定义都是外部定义而不是本地变量。
  • @lefty:你的意思是它们是在文件范围内定义的。 (与块范围、函数范围等相反)内部/外部是具有特定定义的技术术语,具有其他含义。
  • @glglgl:但是说将零初始化数据放入.bss 是一个错误是错误的。当然,在某些系统上,您可以强制编译器以不符合标准的方式运行,但这并不真正相关。

标签: c assembly static


【解决方案1】:

一般来说,bss 部分包含未初始化的值,data 部分包含已初始化的值。但是,gcc 将初始化为零的值放入 bss 部分而不是 data 部分,因为 bss 部分在运行时无论如何都会清零,因此在 @ 中存储零没有多大意义987654327@ 部分,这样可以节省一些磁盘空间,来自 man gcc:

-fno-zero-initialized-in-bss 如果目标支持 BSS 部分,GCC 默认将初始化为零的变量放入 BSS。这 可以在生成的代码中节省空间。此选项关闭此 行为,因为某些程序显式地依赖于变量 数据部分

我不知道为什么.comm 与对象文件本地的静态 存储一起使用,它通常用于声明公共符号,如果不是 定义/初始化,应该由链接器与其他目标文件中具有相同名称的符号合并,这就是为什么在第二个示例中没有使用它,因为变量已初始化,来自 as manual

.comm 声明了一个名为 symbol 的通用符号。链接时,一个常见的 一个目标文件中的符号可以与定义的或通用的符号合并 另一个目标文件中的同名符号

【讨论】:

  • 对我的问题的第二部分有什么想法吗?
  • @lefty:这就是为什么在 Stack Overflow 上同时提出两个问题并不是一个好主意。你不能选择两个答案。
  • 说 BSS 包含未初始化的数据有点误导,因为它实际上是在运行时初始化的。区别主要在于编译时和运行时是否在程序二进制中完成。
  • @fayyazkl 我确实说过它是在运行时初始化的。
  • @mux 我同意。只是强调初始化。我实际上看到了您最初的答案,我觉得没有提到运行时初始化部分。于是我开始写一篇。编辑后看到后者。
【解决方案2】:

第一种情况是因为您将值初始化为零。这是C standard(第 6.7.8 节)的一部分,如果未指定全局整数,则将其初始化为 0。因此,文件格式通过将二进制文件放置在一个特殊部分中来保持二进制文件更小:bss。如果您查看一些ELF specification(在第 I-15 页),您会发现:

.bss 此部分包含未初始化的数据,这些数据有助于程序的 记忆图像。根据定义,系统用零初始化数据 当程序开始运行时。该部分不占用文件空间,因为 由节类型 SHT_NOBITS 指示。

在第一种情况下,编译器进行了优化。它不需要在实际二进制文件中占用空间来存储初始化程序,因为它可以使用bss 段并免费获得您想要的。

现在,您有一个来自外部来源的静态数据这一事实有点有趣(通常不会这样做)。但是,在正在编译的模块中,它不应该与其他模块共享,并且应该用.local 标记。我怀疑它这样做是因为没有为初始化程序存储实际值。

在第二个例子中,因为你给了一个非零的初始化器,它知道它驻留在初始化的数据段data中。 value1 看起来非常相似,但是对于 value2,编译器需要为初始化程序保留空间。在这种情况下,它不需要标记为.local,因为它可以只是放下价值并完成它。它不是全局的,因为它没有 .globl 声明。

顺便说一句,http://refspecs.linuxbase.org/ 是了解有关二进制格式等一些低级细节的好地方。

【讨论】:

    【解决方案3】:

    BSS 是包含在运行时初始化的数据的段,其中数据段包含在程序二进制中初始化的数据。

    现在静态变量总是被初始化,无论是否在程序中显式完成。但是有两个单独的类别,已初始化 (DS) 和未初始化 (BSS) 静态。

    BSS 中存在的所有值都是未在程序代码中初始化的值,因此在运行时加载程序时将其初始化为 0(如果为整数),指针为 null 等。

    因此,当您使用 0 进行初始化时,该值将转到 BSS,因为分配的任何其他值都将分配数据段中的变量。

    一个有趣的结果是,在 BSS 中初始化的数据大小不会包含在程序二进制中,而数据段中的数据大小会包含在内。

    尝试分配一个大的静态数组并在程序中使用它。查看未在代码中显式初始化时的可执行文件大小。然后用非零值初始化它,比如

    static int arr[1000] = {2};
    

    后一种情况下可执行文件的大小会明显变大

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 2012-06-18
      • 2018-04-18
      • 1970-01-01
      相关资源
      最近更新 更多