我一直想知道,在一般情况下,在循环之前声明一个丢弃的变量,而不是在循环内部重复,使任何(性能)差异? Java中的一个(非常无意义的)例子:

A)循环前声明:

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

B)声明(重复)内部循环:

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

哪个更好,a还是b?

我怀疑重复的变量声明(例b)在理论上会产生更多的开销,但编译器足够聪明,所以这无关紧要。例b的优点是更紧凑,并且将变量的作用域限制在使用它的地方。尽管如此,我还是倾向于根据示例a编写代码。

编辑:我对Java案例特别感兴趣。


当前回答

如果你在lambda中使用变量,在c#中是有区别的。但一般来说,编译器基本上会做同样的事情,假设变量只在循环中使用。

鉴于它们基本上是相同的:请注意,版本b对读者来说更明显的是,变量不是,也不能在循环之后使用。此外,版本b更容易重构。在版本a中,将循环体提取到它自己的方法中更加困难。而且,版本b向您保证这样的重构没有副作用。

因此,版本a让我很恼火,因为它没有任何好处,而且它让我更难理解代码……

其他回答

我认为这取决于编译器,很难给出一个一般的答案。

这是个有趣的问题。从我的经验来看,当你为代码争论这个问题时,有一个终极问题需要考虑:

为什么变量需要是全局的?

It makes sense to only declare the variable once, globally, as opposed to many times locally, because it is better for organizing the code and requires less lines of code. However, if it only needs to be declared locally within one method, I would initialize it in that method so it is clear that the variable is exclusively relevant to that method. Be careful not to call this variable outside the method in which it is initialized if you choose the latter option--your code won't know what you're talking about and will report an error.

另外,作为旁注,不要在不同的方法之间重复局部变量名,即使它们的目的几乎相同;这让人很困惑。

我做了一个简单的测试:

int b;
for (int i = 0; i < 10; i++) {
    b = i;
}

vs

for (int i = 0; i < 10; i++) {
    int b = i;
}

我用gcc - 5.2.0编译了这些代码。然后我分解main () 这两个代码的结果是:

1º:

   0x00000000004004b6 <+0>:     push   rbp
   0x00000000004004b7 <+1>:     mov    rbp,rsp
   0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret

vs

   0x00000000004004b6 <+0>: push   rbp
   0x00000000004004b7 <+1>: mov    rbp,rsp
   0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret 

这是完全相同的asm结果。这两种编码的结果不是一样的吗?

我把A和B的例子各运行了20次,循环了1亿次。(jvm - 1.5.0)

A:平均执行时间:0.074秒

B:平均执行时间:0.067秒

令我惊讶的是B稍微快一点。 尽管现在的计算机速度很快,但很难说你是否能准确地测量这一点。 我也会用A的方式来编码,但我想说这并不重要。

A)比B).........更安全想象一下,如果你在循环中初始化结构而不是'int'或'float'然后呢?

就像

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

您肯定会遇到内存泄漏的问题!因此,我认为“A”是更安全的赌注,而“B”是容易受到内存积累的影响,特别是工作在近源库中。你可以在Linux上使用“Valgrind”工具检查,特别是子工具“Helgrind”。