我使用sublime文本作为我的文本编辑器,我总是编写我的for循环,如下所示:
for(int i = 0; i < lengthOfSomething; i++){}
最近我看着编辑器的代码hinter,我注意到它说“Enhaced For Loop”,它向我展示了这个:
for(int i = lengthOfSomething - 1; i >= 0; i--){}
我非常好奇这是如何“增强”的,这样我才能知道并为其他任何可能感到好奇的人知道?
答案 0 :(得分:3)
我怀疑这个问题的答案与性能有很大关系。我在c ++中编写了每个for循环的版本,并使用GCC来获得最终的ASM。 “Enhanced For Loop”(版本2)实际上总共有一条指令subl $1, -8(%rbp)
,而不是“Non Enhanced For Loop”。
无论哪种方式,性能差异都可以忽略不计。这种代码选择应该基于代码的可读性而不管你是否想要在正向或反向的元素上应用for循环内部的操作。 IE从列表的开头或列表的末尾搜索。
for循环的第一个版本:
int main(int argc, char* argv[])
{
int lengthOfSomething = 10;
int valueToInc = 0;
for(int i = 0; i < lengthOfSomething; i++)
{
valueToInc += i;
}
}
产生的ASM
.file "main.cpp"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movl $10, -12(%rbp)
movl $0, -4(%rbp)
movl $0, -8(%rbp)
jmp .L2
.L3:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
addl $1, -8(%rbp)
.L2:
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
jl .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.2 20140206 (prerelease)"
.section .note.GNU-stack,"",@progbits
for循环的第二个版本:
int main(int argc, char* argv[])
{
int lengthOfSomething = 10;
int valueToInc = 0;
for(int i = lengthOfSomething - 1; i >= 0; i--)
{
valueToInc += i;
}
}
产生的ASM
.file "main2.cpp"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movl $10, -12(%rbp)
movl $0, -4(%rbp)
movl -12(%rbp), %eax
subl $1, %eax
movl %eax, -8(%rbp)
jmp .L2
.L3:
movl -8(%rbp), %eax
addl %eax, -4(%rbp)
subl $1, -8(%rbp)
.L2:
cmpl $0, -8(%rbp)
jns .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.2 20140206 (prerelease)"
.section .note.GNU-stack,"",@progbits
答案 1 :(得分:2)
第一个for
循环与第二个for
循环相比,包含一个额外的 Load 操作,该操作在每次迭代时执行(当CPU加载值时)变量lengthOfSomething
)。
话虽如此,如果启用编译器优化,可能会消除此 Load 操作,因为变量lengthOfSomething
的值在循环执行期间保持不变。
通过比较每个循环的反汇编代码,您可以更好地理解这一点。
在第一个循环中,每次迭代都会执行以下操作:
mov eax,dword ptr [i]
add eax,1
mov dword ptr [i],eax
mov eax,dword ptr [i]
cmp eax,dword ptr [lengthOfSomething]
在第二个循环中,每次迭代时都会执行以下操作:
mov eax,dword ptr [i]
sub eax,1
mov dword ptr [i],eax
cmp dword ptr [i],0
如您所见,第一个循环包含额外的mov eax,dword ptr [i]
操作。这是因为CPU架构支持内存内容和常量的比较,但它不支持两个内存内容的比较。请注意,上面示例中的反汇编代码是由Microsoft Visual C ++ 2010的编译器生成的,禁用了编译器优化。但是可以合理地假设其他编译器会生成类似的反汇编代码。
好的,所以在上面的例子中,实际的改进是由于第一个循环比较两个变量(因此其中一个必须加载到寄存器中),而第二个循环比较一个变量和一个常量( CPU架构支持的操作)。但它仍然有一般的推理,即在第二个循环中具有较少的变量访问操作,与第一个循环相比。
答案 2 :(得分:0)
这是旧版编译器和解释器(在旧芯片架构上)的延续,它会比减法稍微慢一点。
现代编译器和解释器(加上现代芯片架构)确实没有这个问题。
有关此问题的详细信息,请参阅JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing。
答案 3 :(得分:0)
如下所示: - http://www.cis.upenn.edu/~matuszek/General/JavaSyntax/enhanced-for-loops.html
逐步浏览数组所有元素的常用方法 顺序是循环的“标准” ,例如,
for (int i = 0; i < myArray.length; i++) {
System.out.println(myArray[i]);
}
所谓的增强的for循环是一种更简单的方法来做同样的事情。 (语法中的冒号可以读作“in。”)
for (int myValue : myArray) {
System.out.println(myValue);
}
在Java 5中引入了增强的for循环作为迭代Collection的所有元素的简单方法(这些页面中未涵盖集合)。它也可以用于数组,如上例所示,但这不是最初的目的。
增强的for循环很简单但不灵活。当您希望以倒数第一顺序遍历数组的元素时,可以使用它们,并且您不需要知道当前元素的索引。在所有其他情况下,循环的“标准”
应该是首选。
另外两种语句类型break
和continue
,也可以控制增强的forloops的行为。
<强> ADVANCED 强>
break和continue语句可以与语句标签一起使用
。有关更多信息: -
https://blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with
答案 4 :(得分:-1)
我不确定这是“增强的循环”是什么。每当我听到增强的for循环时,我会想到像https://blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with这样的东西。