在 C 函数中延迟创建自动变量

Lazy creation of auto variables inside C function

我对这样的 C 代码很好奇:

void test(int y){
   if (y) {
      int x = 1;
      printf("Test: %d", x + y);
   }
   // other important code (don`t use x var)
}

x 变量的必要性取决于 y。 例如:如果我们调用test(0),我们不需要为x变量分配space,因为test(1)调用x变量必须存在于内存中。

现代编译器是否使用这种技术?

编译器更可能的优化是

void test(int y){
   if (y) {
      printf("Test: %d", 1 + y);
   }
   // other important code (don`t use x var)
}

然后在任何一种情况下我们都不需要 x

编译器所做的事情会根据优化级别而有所不同,但人们会期望在最高优化级别下,现代编译器会在寄存器中执行所有操作,其中 x 被常量折叠掉,因为它是一个常量 1 我们可以从 this godbolt session gcc 中看到:

test:
    testl   %edi, %edi
    jne .L4
    rep ret
.L4:
    leal    1(%rdi), %esi
    xorl    %eax, %eax
    movl    $.LC0, %edi
    jmp printf

在最低的优化级别,我们不期望任何聪明的东西,在这种情况下,gcc 为 xy 分配,而不检查是否确实需要 x

subq    , %rsp

正如 Sourav Ghosh 所说,"Nothing you can be certain of!",但是,让我给出编译器可能使用的一些可能性。

首先,从语法上讲,块中的变量仅在块内是已知的。在块之外,编译器(语言)已经完全忘记了它。所以你可以使用这个块构造来拥有一个临时变量。

编译器能做什么?

编译器可能会计算所有块的最大存储量(即函数入口(函数作用域)声明的自动变量加上最大块的大小,加上最大嵌套块的大小,.. . 等)并在堆栈上分配该数量。当一个块退出时,编译器知道它的变量不再存在并且可以为任何后续块重新使用 space (在堆栈上)。在 Fortran 中,这称为覆盖,它相当于 C 中的联合。

同样可能的是,编译器可能会在进入块时在堆栈上分配新的 space(例如 sub sp, 12),并在离开块时释放它(例如 add sp, 12)。

可能还有其他我不知道的策略。