【问题标题】:Lazy creation of auto variables inside C function在 C 函数中延迟创建自动变量
【发布时间】:2016-02-12 18:51:17
【问题描述】:

我对这样的 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变量分配空间,因为test(1)调用x变量必须存在于内存中。

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

【问题讨论】:

  • 不一定。只是为了限制范围。
  • @SouravGhosh 即使我经常调用这个函数(当 y = 0 时)?
  • 也许有一些类似于likely, unlikely 的编译器提示?
  • 编译器将做什么,将随着优化级别的不同而有很大差异(当然还有编译器),请参阅this godbolt 并将-O0 更改为-O3跨度>
  • @kirugan 没有什么可以确定的!! :)

标签: c memory assembly compiler-optimization dynamic-allocation


【解决方案1】:

编译器更可能的优化是

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

那么在这两种情况下我们都不需要x

【讨论】:

    【解决方案2】:

    正如 Sourav Ghosh 所说,“没有什么可以确定的!”但是,让我给出一些编译器可能使用的可能性。

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

    编译器能做什么?

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

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

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

    【讨论】:

    • 此外,在函数执行期间,自动变量可能不会留在堆栈帧中的相同位置。如果变量的值发生变化,编译器可能会决定将值保留在其他位置,并将旧的存储位置用于其他位置。它只需要在有东西指向它时将其固定。
    • @Sourav-Ghosh,哎呀...对不起,更正了拼写。
    【解决方案3】:

    编译器所做的将根据优化级别而有所不同,但人们会期望在最高优化级别上,现代编译器会在寄存器中完成所有操作,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    $32, %rsp
    

    【讨论】:

      猜你喜欢
      • 2012-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多