C语言中的局部变量和静态变量

C语言中的局部变量和静态变量,c,assembly,static,C,Assembly,Static,在编译此文件时: // 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 .co

在编译此文件时:

// 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
我的问题是:

  • 为什么在第一种情况下,在bss段中分配值,而在第二种情况下,在数据段中分配值
  • 为什么value2变量在第一种情况下定义为.local和.comm,而在第二种情况下不定义为.local和.comm

  • 一般来说,
    bss
    部分包含未初始化值,
    data
    部分包含初始化值。但是,gcc将初始化为零的值放入
    bss
    部分,而不是
    数据
    部分,因为
    bss
    部分在运行时已归零,所以在
    数据
    部分存储零没有多大意义,这节省了一些磁盘空间

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

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

    .comm声明一个名为symbol的公共符号。链接时,一个常见的 一个对象文件中的符号可以与定义的或公共的符号合并 在另一个对象文件中具有相同名称的符号

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

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

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

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

    一个有趣的结果是,在BSS中初始化的数据的大小将不包括在程序二进制中,其中包括数据段中的数据的大小

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

    static int arr[1000] = {2};
    

    在后一种情况下,可执行文件的大小将大大增加。第一种情况是因为您将值初始化为零。这是(第6.7.8节)的一部分,如果未指定,则全局整数将被初始化为0。因此,文件格式通过将二进制文件放在一个特殊的部分:
    bss
    ,使二进制文件更小。如果你看一看(第I-15页),你会发现:

    .bss此部分保存有助于程序运行的未初始化数据 内存映像。根据定义,系统用零初始化数据 当程序开始运行时。该节不占用任何文件空间,如下所示 由截面类型SHT_NOBITS指示

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

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

    在第二个示例中,由于给定了一个非零初始值设定项,它知道驻留在初始化的数据段
    data
    value1
    看起来非常相似,但对于
    value2
    ,编译器需要为初始值设定项保留空间。在这种情况下,不需要将其标记为
    .local
    ,因为它可以只放置值并使用它来完成。它不是全局的,因为它没有
    .globl
    语句


    顺便说一句,对于一些关于二进制格式之类的低级细节来说,这是一个不错的去处。

    很难从您的C代码片段中分辨出来,但是
    value1
    似乎是一个全局变量(而不是局部变量)。局部变量将在堆栈上分配。很难说。我会考虑把它放进<代码>。BSS 一个bug。虽然与标准视图没有区别(未初始化的变量应初始化为
    0
    ),“初始化到
    0
    与未初始化不同,这已经成为一种习惯。@Codo我的代码段中的两个变量定义都是外部定义,而不是局部变量。@lefty:那么你的意思是它们是在文件范围内定义的。”。(与块作用域、函数作用域等相反)内部/外部是具有特定定义的技术术语,具有其他含义。@glglgl:但是说将零初始化数据放入
    .bss
    是错误的。当然,在某些系统上,您可以强制编译器以不符合标准的方式运行,但这并不重要。关于我问题的第二部分,您有什么想法吗?@lefty:这就是为什么在堆栈溢出问题上一次问两个问题不是一个好主意的原因。您不能选择两个答案。说BSS包含未初始化的数据有点误导,因为它实际上是在r处初始化的