20

还是全部与语义有关?

4

16 回答 16

64

简短的回答:不,它们完全相同。

猜猜它在理论上可能取决于编译器;一个真正破碎的人可能会做一些稍微不同的事情,但我会感到惊讶。

只是为了好玩,这里有两个变体,它们使用 Ubuntu 附带的 x86 gcc 版本 4.3.3 编译成完全相同的汇编代码。您可以在 linux 上使用objdump检查最终二进制文件中生成的程序集。

主函数()
{
#如果 1
    诠释 i = 10;
    做 { printf("%d\n", i); } 当我);
#别的
    诠释 i = 10;
    for (; i; --i) printf("%d\n", i);
#万一
}

编辑:这是一个“带橙子的橙子”while 循环示例,它也可以编译为相同的内容:

    while(i) { printf("%d\n", i);  - 一世; }
于 2009-05-11T12:38:39.053 回答
14

如果你的 for 和 while 循环做同样的事情,编译器生成的机器代码应该(几乎)相同。

例如在我几年前做的一些测试中,

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

int i = 0;
do
{
  ...
  i++;
}
while (i < 10);

将生成完全相同的代码,或者(并且 Neil 在评论中指出)带有一个额外的 jmp,这不会对性能产生足够大的影响而无需担心。

于 2009-05-11T12:31:34.843 回答
8

没有语义差异,也不需要任何编译差异。但这取决于编译器。所以我尝试使用 g++ 4.3.2、CC 5.5 和 xlc6。

g++,CC 是相同的,xlc 不是

xlc 的不同之处在于初始循环条目。

extern int doit( int );
void loop1( ) {
  for ( int ii = 0; ii < 10; ii++ ) {
    doit( ii );
  }
}

void loop2() {
  int ii = 0; 
  while ( ii < 10 ) {
    doit( ii );
    ii++;
  }
}

XLC 输出

.loop2:                                 # 0x00000000 (H.10.NO_SYMBOL)
    mfspr   r0,LR
    stu     SP,-80(SP)
    st      r0,88(SP)
    cal     r3,0(r0)
    st      r3,64(SP)
    l       r3,64(SP)  ### DIFFERENCE ###
    cmpi    0,r3,10
    bc      BO_IF_NOT,CR0_LT,__L40
...
enter code here
.loop1:                                 # 0x0000006c (H.10.NO_SYMBOL+0x6c)
    mfspr   r0,LR
    stu     SP,-80(SP)
    st      r0,88(SP)
    cal     r3,0(r0)
    cmpi    0,r3,10    ### DIFFERENCE ###
    st      r3,64(SP)
    bc      BO_IF_NOT,CR0_LT,__La8
...
于 2009-05-11T13:49:30.777 回答
7

循环测试中变量的while范围比循环头中声明的变量范围要宽for

因此,如果将变量保持更长时间作为副作用存在性能影响,那么在 while 和 for 循环之间进行选择(而不是将 while 包装在 {} 中以减小其范围)将会产生性能影响变量)。

一个例子可能是一个并发集合,它计算引用它的迭代器的数量,如果存在多个迭代器,它会应用锁定来防止并发修改,但是如果只有一个迭代器引用它,作为优化会忽略锁定。如果您for在同一个容器上使用不同命名的迭代器的函数中有两个循环,则将采用快速路径,但如果有两个while循环,则将采用慢速路径。同样,如果对象很大(更多缓存流量)或使用系统资源,则可能会影响性能。但我想不出一个真实的例子,我见过它会在哪里产生影响。

于 2009-05-11T13:11:07.730 回答
6

使用循环展开进行优化的编译器可能只会在 for 循环的情况下这样做。

于 2009-05-11T13:42:54.580 回答
5

两者是等价的。这是语义问题。

唯一的区别可能在于 do...while 构造,您将条件的评估推迟到主体之后,因此可以节省 1 次评估。

i = 1; do { ... i--; } while( i > 0 ); 

for(  i = 1; i > 0; --i )
{ ....
}  
于 2009-05-11T12:32:26.573 回答
4

我写编译器。if我们将所有“结构化”控制流( , while, for, switch, do... )编译while成条件分支和无条件分支。然后我们分析控制流图。由于 C 编译器无论如何都必须处理一般goto情况,因此最容易将所有内容简化为分支和条件分支指令,然后一定要处理好这种情况。(AC 编译器不仅要处理手写代码,还要处理自动生成的代码,这些代码可能有很多很多goto语句。)

于 2009-05-13T03:37:07.460 回答
3

不。如果他们在做相同的事情,他们会编译成相同的代码——正如你所说,这是关于语义的。选择最能代表您想要表达的内容。

于 2009-05-11T12:32:16.637 回答
3

理想情况下它应该是相同的,但最终取决于您的编译器/解释器。可以肯定的是,您必须测量或检查生成的汇编代码。

证明可能存在差异:这些行使用 cc65 生成不同的汇编代码。

for (; i < 1000; ++i);   
while (i < 1000) ++i;
于 2009-05-11T13:02:49.137 回答
1

在 Atmel ATMega 上,while() 比 for() 快。为什么在 AVR035: Efficient C Coding for AVR 中解释了这一点。

PS 原始平台没有被提及。

于 2009-05-11T13:48:07.847 回答
1

补充另一个答案:根据我的经验,优化软件就像从男人身上剃掉浓密的大胡子。

  • 首先,你用剪刀将它切成大块(从呼叫树上剪掉整个四肢)。
  • 然后你用电动剪短(调整算法)做短。
  • 最后你用剃须刀刮掉最后一点点(低级优化)。

for()最后一点是两者之间的差异while()可能(但可能不会)产生差异。

PS 我认识的程序员(他们都非常好,我怀疑是一个有代表性的样本)基本上是从另一个方向开始的。

于 2009-05-12T16:49:03.067 回答
1

continue在forwhile中表现不同:在for 中,它会改变计数器,在while中,它通常不会

于 2009-05-12T13:33:19.427 回答
0

在某些情况下存在差异。

如果您正处于这种差异很重要的地步,您要么需要选择更好的算法,要么开始使用汇编语言进行编码。相信我,汇编代码比修复编译器版本更可取。

于 2009-05-12T18:08:29.410 回答
0

就性能而言,它们是相同的。我倾向于while在等待状态更改(例如等待缓冲区被填充)和for处理多个离散对象(例如遍历集合中的每个项目)时使用。

于 2009-05-11T12:46:02.220 回答
0

比 快/while()慢?for()让我们回顾一下关于优化的一些事情:

  • 编译器编写者非常努力地通过减少对跳转、比较、递增和它们生成的其他类型指令的调用来减少周期。

  • 另一方面,调用指令消耗的周期要多得多,但编译器几乎无能为力来删除它们。

  • 作为程序员,我们编写了很多函数调用,有些是因为我们的本意,有些是因为我们很懒,还有一些是因为编译器在不明显的情况下将它们插入。

  • 大多数时候,这并不重要,因为硬件如此之快,而我们的工作如此之少,以至于计算机就像一只狼吞虎咽地乞求更多的小猎犬。

  • 然而,有时工作量很大,以至于性能是一个问题。

  • 那我们怎么办?更大的回报在哪里?

    • 让编译器减少循环等几个周期?
    • 寻找不需要做那么多的函数调用?
  • 编译器不能做后者。只有我们程序员可以。

  • 我们需要学习或被教导如何做到这一点。它不是自然而然的。我们天生倾向于做出错误的猜测,然后押注它们。获得更好的算法是一个开始,但只是一个开始。我们的老师需要教这个,如果他们确实知道怎么做的话。

  • 探查器是一个开始。我这样做。

当被问及为什么抢劫银行时,威利萨顿的杜撰引述 :
因为那是钱的所在。

如果你想保存周期,找出它们在哪里。

于 2009-05-13T18:44:13.617 回答
-4

可能只有编码风格。

  • 因为如果你知道迭代次数。
  • 而如果您不知道迭代次数。
于 2009-05-11T12:32:37.273 回答