5

如果我执行以下操作之一(我认为对于这个问题来说是等效的)

for(int i=0; i<A; i++)
{
  //... do stuff
  for(int j=0; j<B; j++)
  {
    //... do stuff
  }
  //... do stuff
}

for(int i=0; i<A; i++)
{
  int j;
  //... do stuff
}

变量是否在j每个循环中都在堆栈上重新创建(每次循环都在SP不断更新),或者编译器是否足够聪明,可以知道一个函数一次可能有多少个局部变量,然后为所有这些变量腾出空间函数入口处的堆栈?

我知道这在理论上是依赖于编译器的,但我假设像这样的简单事情在所有主要编译器中都很常见。如果没有,有人具体了解编译器吗GCCVC++

4

4 回答 4

9

它是有效的。gcc将优化它,就像大多数现代编译器一样。还要记住 Donald Knuth 所说的话:

我们应该忘记小的效率,比如大约 97% 的时间:过早优化是万恶之源。

您可以通过比较汇编代码来检查它是否有效。使用例如diff进行比较。要生成程序集使用-S标志,gcc -S. 它相当于gcc -S -O0,因为默认优化级别为 0。因此,即使在最低级别的优化中,gcc也会为您处理此变量。

第一个版本(为了更好的可读性更喜欢这种方式):

#include <stdio.h>

int main()
{
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            printf("%d ", i + j);
        }
    }
    return 0;
}

第二个版本:

#include <stdio.h>

int main()
{
    for (int i = 0; i < 10; i++)
    {
        int j;
        for (j = 0; j < 10; j++)
        {
            printf("%d ", i + j);
        }
    }
    return 0;
}

相同的组装结果:

    .file   "main.cpp"
    .section    .rodata
.LC0:
    .string "%d "
    .text
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    jmp .L2
.L5:
    movl    $0, -8(%rbp)
    jmp .L3
.L4:
    movl    -8(%rbp), %eax
    movl    -4(%rbp), %edx
    leal    (%rdx,%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    addl    $1, -8(%rbp)
.L3:
    cmpl    $9, -8(%rbp)
    setle   %al
    testb   %al, %al
    jne .L4
    addl    $1, -4(%rbp)
.L2:
    cmpl    $9, -4(%rbp)
    setle   %al
    testb   %al, %al
    jne .L5
    movl    $0, %eax
    leave
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

int i-4(%rbp)int j-8(%rbp)。如您所见int j,没有重新分配或其他东西。

于 2013-05-26T17:11:22.477 回答
1

我相信变量只会被创建一次,尽管我不在乎,我也不相信你应该这样做。

这可能是您进行预优化(或不必要的优化)的一个示例;通过在循环中声明变量所造成的潜在低效率非常小,并且通过在不同位置声明变量来“优化”代码将对程序的整体运行时和内存使用产生微不足道的影响。

考虑花时间优化你的算法和寻找有效的数据结构,因为这可能会更好地利用你的时间。

于 2013-05-26T17:13:33.277 回答
0

今天的编译器太强大了,无法优化这些东西。因此,请不要打扰并根据您的方便使用。过早的优化更邪恶。

于 2013-05-26T17:10:26.800 回答
0

为什么不自己试试看呢?

class foo {
public:
    foo () { std::cout << "Construct\n"; }
    ~foo () { std::cout << "Destruct\n"; }
    };

int main () {
    for ( int i = 0; i < 10; ++i ) {
        foo f;
    }

    return 0;
}

您会看到f每次循环都会调用构造函数(和析构函数!)。因此,您的问题的答案是“是的,每次通过循环都会重新创建变量。”

回到您的示例,您正在声明一个 int,它有一个什么都不做的构造函数和析构函数。因此,在循环中声明 int 不会降低性能。

于 2013-05-26T17:11:20.343 回答