C++ 编译器将做什么
我已经编程好几年了,但令人尴尬的是,有一两件事我还没有完全弄清楚 在下面刚刚用作示例的基本代码中,当编译器遇到myFunc()时,str1和str2将存储在哪里 它们是指向字符串文本的指针,所以我假设字符串文本将存储在只读内存中,但是在这种情况下,一个指针是静态本地的,另一个指针不是静态本地的,有什么区别呢? 此外,我认为局部变量将存储在堆栈上,并且在调用函数之前不会分配它们?这令人困惑 对于整数var1,它是非静态的,但是var2是静态的。编译器会在编译时将这个var2放在数据段中。我在另一篇文章中读到,本地静态变量将在第一次使用时创建和初始化,而不是在编译期间。那么在这种情况下,如果函数从未被调用呢 提前感谢您有经验的知识 已编辑:从main()调用myFunc()。这是一个输入错误,因为myFunc()从未被调用过C++ 编译器将做什么,c++,c,function,static,compilation,C++,C,Function,Static,Compilation,我已经编程好几年了,但令人尴尬的是,有一两件事我还没有完全弄清楚 在下面刚刚用作示例的基本代码中,当编译器遇到myFunc()时,str1和str2将存储在哪里 它们是指向字符串文本的指针,所以我假设字符串文本将存储在只读内存中,但是在这种情况下,一个指针是静态本地的,另一个指针不是静态本地的,有什么区别呢? 此外,我认为局部变量将存储在堆栈上,并且在调用函数之前不会分配它们?这令人困惑 对于整数var1,它是非静态的,但是var2是静态的。编译器会在编译时将这个var2放在数据段中。我在另一篇
int myFunc()
{
static char* str1 = "Hello";
char* str2 = "World";
int var1 = 1;
static int var2 = 8;
}
int main()
{
return myFunc();
}
编译器将生成一个不接受输入、不执行任何操作、然后不输出的程序 所有这些声明都是完全无关的,因为它们对程序的[不存在的]结果没有任何贡献。您可能会说它们“得到了优化”,但事实是,它们实际上在生成的编译可执行文件中没有类似的内容
static const char* str1 = "Hello";
str1
是指向将存储在只读内存中的字符串文本的静态本地指针
const char* str2 = "World";
str2
是一个本地“堆栈分配”指针,指向将存储在只读内存中的字符串文本
const char* str2 = "World";
str1
和str2
的值分别是它们所指向的字符串文本的地址
int var1 = 1;
static int var2 = 8;
如果从未达到这些代码行,
var2
将永远不会初始化。我不知道编译器是否在编译时在其他地方为它预留了一块内存。静态变量,即使是函数范围内的变量,也将存储在全局范围内。函数或作用域内的静态
变量将仅在输入该函数或作用域的第一次时初始化。在大多数编译器中,当输入函数作用域时,非
静态变量将被分配或存储在堆栈上,当输入作用域时,变量将被初始化。某些编译器将局部变量存储在其他地方。编辑:
另一个答案和注释是正确的,因为你的变量没有被使用,所以你的变量会被优化。但是让我们玩一玩,用它们来看看会发生什么
我使用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();
}
在程序集文件中甚至找不到var1。它实际上只是一个加载到堆栈中的常量
在程序集文件的顶部,我们在.rdata部分看到“World”(str2)。在程序集文件的下方,字符串“Hello”位于.rdata部分,而str1的标签(包含“Hello”的标签或地址)位于.data部分。var2也在.data部分
下面是一个更深入地探究为什么会发生这种情况的例子
指出.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;
}
这看起来更像原作。var1仍然优化为仅使用常量,但var2现在再次位于.data部分。“Hello”和“World”仍然在.rdata部分,因为它们是常量
其中一条评论指出,在使用不同编译器的不同平台上,这将是不同的。我鼓励您尝试一下。您的代码在编译时至少会有一个警告,因为函数不会返回任何与返回类型规范相矛盾的内容 无论如何,在我的机器上,它会生成代码。如果您不使用任何优化代码,则会为分配本地
str2
的函数发出优化代码<代码>str1和var2
在代码的数据部分分配,以指向各自的值。如果使用优化,显然会发出一个愚蠢的代码,而未指定的局部变量会作为未使用的全局变量消失
要观察这一点,您至少可以使用nm
检查目标代码:
$ gcc -o p p.c
$ nm p
0000000100000f90 T _main
0000000100000f70 T _myFunc
0000000100001000 d _myFunc.str1
0000000100001008 d _myFunc.var2
$ gcc -O3 -o p2 p.c
$ nm p2
0000000100000fb0 T _main
0000000100000fa0 T _myFunc
如果您想了解更多详细信息,请使用
-S
生成汇编代码并观察发生的情况。编译器所做的工作必须基于(假设编译器工作正常)代码的语义,因此我将讨论这一点
首先,一个相当次要的问题。通过使用()
声明函数,可以指定它接受固定但未指定数量和类型的参数。这是一种过时的声明/定义形式,很少有好的理由使用它。(空括号在C++中有不同的含义,但你询问C.)指定函数没有参数,使用<代码>(空隙)< /C> >而不是<代码>()>代码>(特别是对于<代码>主< /代码>,因为它不是100%明确的,<代码> int()/<代码必须被一致的编译器接受)。
随着这一变化:
int myFunc(void)
{
static char* str1 = "Hello";
char* str2 = "World";
int var1 = 1;
static int var2 = 8;
}
int main(void)
{
return myFunc();
}
这个节目
int myFunc(void)
{
static char* str1 = "Hello";
char* str2 = "World";
int var1 = 1;
static int var2 = 8;
}
int main(void)
{
return myFunc();
}
static char* str1 = "Hello";
static const char *str1 = "hello";
char* str2 = "World";
int var1 = 1;
static int var2 = 8;