c++ - 编译器会做什么

标签 c++ c function static compilation

我已经编程了几年,但令人尴尬的是,有一两件事我仍然不完全清楚。

下面的基本代码只是作为例子,当编译器遇到myFunc()时,str1和str2会存放到哪里呢?

它们是指向字符串文字的指针,所以我假设字符串文字将存储在只读内存中,但在这种情况下,一个指针是静态本地指针而另一个不是静态本地指针有什么区别? 另外,我认为局部变量将存储在堆栈中,并且在调用函数之前不会分配它们?这令人困惑。

对于整数 var1,它是非静态的,但 var2 是静态的。编译器会不会在编译的时候把这个var2放到data段中。我读过另一篇文章 When do function-level static variables get allocated/initialized? ,局部静态变量将在第一次使用时创建和初始化,而不是在编译期间。那么在那种情况下,如果函数从未被调用怎么办?

提前感谢经验丰富的知识。

已编辑:从 main() 调用 myFunc()。这是一个打字错误,因为 myFunc() 甚至从未被调用过

int myFunc()
{
    static char* str1 = "Hello";
    char* str2 = "World";

    int var1 = 1;
    static int var2 = 8;

}

int main()
{

    return myFunc();
}

最佳答案

编辑:

其他答案和评论是正确的 - 实际上,您的变量将被优化掉,因为它们甚至没有被使用。但让我们找点乐子,实际使用它们看看会发生什么。

我使用 gcc -S trial.c 按原样编译了 op 的程序,尽管从未调用过 myFunc,但此答案没有其他任何变化。

我稍微修改了您的程序以实际使用这些变量,这样我们就可以更多地了解编译器和链接器将做什么。在这里:

#include <stdio.h>

int myFunc()
{
    static const char* str1 = "Hello";
    const char* str2 = "World";

    int var1 = 1;
    static int var2 = 8;
    printf("%s %s %d %d\n", str1, str2, var1, var2);
    return 0;
}

int main()
{
    return myFunc();
}

我用 gcc -S trial.c 编译并得到以下汇编文件:

    .file   "trial.c"
    .section .rdata,"dr"
.LC0:
    .ascii "World\0"
.LC1:
    .ascii "%s %s %d %d\12\0"
    .text
    .globl  myFunc
    .def    myFunc; .scl    2;  .type   32; .endef
    .seh_proc   myFunc
myFunc:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $64, %rsp
    .seh_stackalloc 64
    .seh_endprologue
    leaq    .LC0(%rip), %rax
    movq    %rax, -8(%rbp)
    movl    $1, -12(%rbp)
    movl    var2.3086(%rip), %edx
    movq    str1.3083(%rip), %rax
    movl    -12(%rbp), %r8d
    movq    -8(%rbp), %rcx
    movl    %edx, 32(%rsp)
    movl    %r8d, %r9d
    movq    %rcx, %r8
    movq    %rax, %rdx
    leaq    .LC1(%rip), %rcx
    call    printf
    movl    $0, %eax
    addq    $64, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .def    __main; .scl    2;  .type   32; .endef
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
    call    myFunc
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .data
    .align 4
var2.3086:
    .long   8
    .section .rdata,"dr"
.LC2:
    .ascii "Hello\0"
    .data
    .align 8
str1.3083:
    .quad   .LC2
    .ident  "GCC: (Rev1, Built by MSYS2 project) 5.4.0"
    .def    printf; .scl    2;  .type   32; .endef

var1 甚至没有在程序集文件中找到。它实际上只是一个加载到堆栈上的常量。

在程序集文件的顶部,我们在 .rdata 部分中看到“World”(str2)。在汇编文件的下方,字符串“Hello”位于 .rdata 部分,但 str1 的标签(包含“Hello”的标签或地址)位于 .data 部分。 var2 也在 .data 部分。

这是 a stackoverflow question这更深入地研究了为什么会发生这种情况。

Another stackoverflow question指出 .rdata 部分是 .data 的只读部分,并解释了不同的部分。

希望这对您有所帮助。


编辑:

我决定尝试使用 -O3 编译器标志(高度优化)。这是我得到的程序集文件:

    .file   "trial.c"
    .section .rdata,"dr"
.LC0:
    .ascii "World\0"
.LC1:
    .ascii "Hello\0"
.LC2:
    .ascii "%s %s %d %d\12\0"
    .section    .text.unlikely,"x"
.LCOLDB3:
    .text
.LHOTB3:
    .p2align 4,,15
    .globl  myFunc
    .def    myFunc; .scl    2;  .type   32; .endef
    .seh_proc   myFunc
myFunc:
    subq    $56, %rsp
    .seh_stackalloc 56
    .seh_endprologue
    leaq    .LC0(%rip), %r8
    leaq    .LC1(%rip), %rdx
    leaq    .LC2(%rip), %rcx
    movl    $8, 32(%rsp)
    movl    $1, %r9d
    call    printf
    nop
    addq    $56, %rsp
    ret
    .seh_endproc
    .section    .text.unlikely,"x"
.LCOLDE3:
    .text
.LHOTE3:
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.unlikely,"x"
.LCOLDB4:
    .section    .text.startup,"x"
.LHOTB4:
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    subq    $40, %rsp
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
    xorl    %eax, %eax
    addq    $40, %rsp
    ret
    .seh_endproc
    .section    .text.unlikely,"x"
.LCOLDE4:
    .section    .text.startup,"x"
.LHOTE4:
    .ident  "GCC: (Rev1, Built by MSYS2 project) 5.4.0"
    .def    printf; .scl    2;  .type   32; .endef

var1 现在只是一个放在寄存器 (r9d) 中的常量 1。 var2 也只是一个常量,但它放在堆栈上。此外,以更直接(高效)的方式访问字符串“Hello”和“World”。

所以,我决定尝试一些稍微不同的东西:

#include <stdio.h>

void myFunc()
{
    static const char* str1 = "Hello";
    const char* str2 = "World";

    int var1 = 1;
    static int var2 = 8;
    printf("%s %s %d %d\n", str1, str2, var1, var2);

    var1++;
    var2++;
    printf("%d %d", var1, var2);
}

int main()
{
    myFunc();
    myFunc();
    return 0;
}

以及使用 gcc -O3 -S trial.c 的关联程序集

    .file   "trial.c"
    .section .rdata,"dr"
.LC0:
    .ascii "World\0"
.LC1:
    .ascii "Hello\0"
.LC2:
    .ascii "%s %s %d %d\12\0"
.LC3:
    .ascii "%d %d\0"
    .section    .text.unlikely,"x"
.LCOLDB4:
    .text
.LHOTB4:
    .p2align 4,,15
    .globl  myFunc
    .def    myFunc; .scl    2;  .type   32; .endef
    .seh_proc   myFunc
myFunc:
    subq    $56, %rsp
    .seh_stackalloc 56
    .seh_endprologue
    movl    var2.3086(%rip), %eax
    leaq    .LC0(%rip), %r8
    leaq    .LC1(%rip), %rdx
    leaq    .LC2(%rip), %rcx
    movl    $1, %r9d
    movl    %eax, 32(%rsp)
    call    printf
    movl    var2.3086(%rip), %eax
    leaq    .LC3(%rip), %rcx
    movl    $2, %edx
    leal    1(%rax), %r8d
    movl    %r8d, var2.3086(%rip)
    addq    $56, %rsp
    jmp printf
    .seh_endproc
    .section    .text.unlikely,"x"
.LCOLDE4:
    .text
.LHOTE4:
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.unlikely,"x"
.LCOLDB5:
    .section    .text.startup,"x"
.LHOTB5:
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    subq    $40, %rsp
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
    call    myFunc
    call    myFunc
    xorl    %eax, %eax
    addq    $40, %rsp
    ret
    .seh_endproc
    .section    .text.unlikely,"x"
.LCOLDE5:
    .section    .text.startup,"x"
.LHOTE5:
    .data
    .align 4
var2.3086:
    .long   8
    .ident  "GCC: (Rev1, Built by MSYS2 project) 5.4.0"
    .def    printf; .scl    2;  .type   32; .endef

这看起来有点像原来的样子。 var1 仍然优化为常量,但 var2 现在再次位于 .data 部分。 “Hello”和“World”仍在 .rdata 部分中,因为它们是常量。

其中一条评论指出,在具有不同编译器的不同平台上,这会有所不同。我鼓励您尝试一下。

关于c++ - 编译器会做什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39515911/

相关文章:

C++11 Variadic Templates : 3. .n int's separated into first, middle, last

c - 在动态 bool 数组上使用 memset 是否定义明确?

algorithm - 你能帮我确定这个应用程序的四舍五入吗?

bash - 如何从 shell 调用 .bashrc 中定义的函数?

c++ - get() 的异常行为(从 C++ 中的文件读取)

c++ - 是否可以在不使指针失效的情况下调整/重新分配大块内存?

c++ - 如何为此正确编写析构函数

c - 检测到堆损坏 - 使用字符串实现合并排序

c - MSP430 上的 C 程序所需的最大堆栈大小

javascript - 如何在循环中循环此 javascript 代码