C++ 编译器是否删除仅用于使代码更清晰的 const 变量

K. *_*los 4 c++ memory-management constants function compiler-optimization

假设你有一个函数

void func(int a, int b, int c, int d);
Run Code Online (Sandbox Code Playgroud)

现在,当你调用这个函数时,因为它有很多参数,而不是像这样调用它:

func(1, 2, 3, 4);
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

const int a = 1;
const int b = 2;
const int c = 3;
const int d = 4;

func(a, b, c, d);
Run Code Online (Sandbox Code Playgroud)

更好地理解每个值是什么。

所以问题是:C++ 编译器只是用第一种方式替换第二种方式,还是实际上为堆栈中的填充变量分配空间,浪费内存和时间?

Jan*_*tke 8

编译器可以优化 - 并且会尝试使用正确的标志尽可能地优化你的代码。这包括消除你的const ints。这就是原因及其工作原理:

\n

假设规则

\n
\n

[...]相反,需要一致的实现来模拟(仅)抽象机的可观察行为,如下所述。6 \n[...]

\n

6)此规定有时称为 \xe2\x80\x9cas-if\xe2\x80\x9d 规则,因为只要结果就像已遵守该要求,实现就可以自由地忽略本文档的任何要求,从程序的可观察行为可以确定。[...]

\n
\n

-介绍.摘要\xc2\xa71

\n

或者简单地说:如果你的代码的行为没有改变,编译器可以用它做任何它想做的事情以使其更快。

\n

另请参阅:“假设”规则到底是什么?

\n

针对您的示例的编译器优化

\n
const int a = 1;\nconst int b = 2;\nconst int c = 3;\nconst int d = 4;\n\nfunc(a, b, c, d);\n
Run Code Online (Sandbox Code Playgroud)\n

四个变量中的每一个都声明一个对象,并且对象占用内存。\n直觉告诉我们,堆栈需要4 * sizeof(int)更大的字节才能存储所有对象。

\n

然而,我们没有使用任何这些对象的地址,我们只是用它们调用一个函数,所以下面的代码:

\n
void func(int a, int b, int c, int d);\n\nvoid foo() {\n    const int a = 1;\n    const int b = 2;\n    const int c = 3;\n    const int d = 4;\n\n    func(a, b, c, d);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译为:(请参阅使用 GCC 13 的 CE 示例-O2

\n
foo():\n        mov     ecx, 4\n        mov     edx, 3\n        mov     esi, 2\n        mov     edi, 1\n        jmp     func(int, int, int, int)\n
Run Code Online (Sandbox Code Playgroud)\n

注意:如果优化较少或没有优化(例如-O0),您将得到截然不同的程序集,所有四个ints 都驻留在堆栈上。

\n

我们得到的程序集我们编写的程序相同:

\n
foo():\n        mov     ecx, 4\n        mov     edx, 3\n        mov     esi, 2\n        mov     edi, 1\n        jmp     func(int, int, int, int)\n
Run Code Online (Sandbox Code Playgroud)\n

所以就好像我们从未声明过这些对象并且从未占用内存,而只是使用没有地址的临时对象1, 2, 34\n这种优化是允许的,因为可观察到的行为仍然是相同的:我们仍然使用相同的参数进行调用func(由于我们的调用约定,通过寄存器传递)。

\n

一般来说,我建议使用Compiler Explorer来查看编译器生成的程序集类型。\n它可以帮助您了解许多优化(或错过的优化)。

\n