C for循环的实现方式与其他语言不同?

时间:2008-10-23 13:16:53

标签: c++ c knuth

我在回顾Knuth的“计算机程序设计的艺术”时阅读了以下内容:

“非常'实用性'意味着潜在的CS专业必须学习Kernighan在设计C时的错误,特别是一个臭名昭着的事实,即一个for循环反复评估for条件,这个重复的同时并且不能匹配大多数实现for循环的其他语言。“

http://www.amazon.com/review/R9OVJAJQCP78N/ref=cm_cr_pr_viewpnt#R9OVJAJQCP78N

这家伙在说什么?你怎么能实现一个for循环不仅仅是一个while循环的语法糖?

11 个答案:

答案 0 :(得分:29)

考虑一下:

for i:=0 to 100 do { ... }

在这种情况下,我们可以通过函数调用替换最终值100:

for i:=0 to final_value() do { ... }

... final_value - 函数只会被调用一次。

但是在C中:

for (int i=0; i<final_value(); ++i) // ...

... final_value - 函数将在循环中的每次迭代中被调用,因此更好的做法是更加详细:

int end = final_value();
for (int i=0; i<end; ++i) // ...

答案 1 :(得分:5)

如果你想要的只是一个简单的计数循环,那么

for (i=0; i<100; i++) dostuff();

会很好,编译器可以优化它。

如果在for语句的continue部分使用函数,例如

for (i=0; i<strlen(s); i++) dostuff();

然后每次都会评估该函数,这通常不是一个好主意,因为函数开销会减慢您的进程。有时它可能会使您的过程变得无法使用。

如果函数的返回值在迭代期间不会改变,则从循环中提取它:

slen = strlen(s);
for (i=0; i<slen; i++) dostuff();

但是有时候函数会在每次调用时返回不同的值,然后你不希望它从循环中提取出来:

for (isread(fd, &buffer, ISFIRST);
     isstat(fd) >= 0;
     isread(fd, &buffer, ISNEXT)
{
  dostuff(buffer);
}

并且您希望每次都进行评估。 (这是一个基于我所做工作的略有做法的例子,但它显示了潜力)。

C为您提供了以任何方式滚动循环的原始能力。您必须知道循环应该如何工作,并根据您的需要尽可能地优化它。

最后一个例子可能被表达为while循环:

isread(fd, &buffer, ISFIRST);
while (isstat(fd) >= 0)
{
  dostuff(buffer);
  isread(fd, &buffer, ISNEXT);
}

但它不是很整洁,如果我在循环中使用continue,那么我必须再次调用迭代的isread。将整个事物放在for循环中使它更整洁,并确保每次循环都调用迭代isread。

我编写了较低级别的函数,因此它们可以用于这样的循环。它将while循环的所有元素组合在一起,以便您更容易理解它。

答案 2 :(得分:4)

他可能指的是像for i:=0 to N这样的循环,以及遍历集合元素的每个循环。我怀疑所有具有C风格for循环的语言实际上是从C语言中获得的。

答案 3 :(得分:3)

马格努斯说得对,但是人们也应该注意到,在大多数语言(前C)中,条件是 end 标准(即“当我等于100时停止”)。在C(和大多数后C语言)中,它是 continue 标准(即,“当我小于100时继续”)。

答案 4 :(得分:2)

循环展开或许?如果您知道for循环执行的时间,您可以逐字复制并粘贴循环的内容。大多数while循环将基于一些不是从0到N的简单计数的条件,因此将无法使用此优化。

以此为例

int x;
for (x = 10; x != 0; --x)
{
    printf ("Hello\n");
}

我知道你通常会做x = 0; x <= 10; ++x,但所有这些都将在集会中公布。

一些伪装配:

mov 10, eax
loop:
print "hello"
dec eax
jne loop

在这个例子中,我们继续跳回循环以打印“你好”10次。但是,我们每次都在循环中使用jne指令来评估条件。

如果我们展开它,我们可以简单地把

print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"

我们不需要任何其他说明,因此速度更快。这只是一个简单的例子 - 我会试着找到一个更好的例子!

答案 5 :(得分:2)

基本上,C(以及Java,JavaScript和许多C派生语言)for循环确实是while循环的语法糖:

for (int i = 0; i < max; i++) { DoStuff(); }

这是一个非常流行的习语,严格等同于:

int i = 0; while (i < max) { DoStuff(); i++; }

(分开范围问题,无论如何在C版本之间发生了变化。)

每次迭代都会评估停止条件。在某些情况下它可能很有趣,但它可能是一个陷阱:我在源中看到i < strlen(longConstantString),这是减慢程序速度的主要方法,因为strlen在C中是一个代价高昂的函数。
不知何故,for循环主要设计为提前运行多次,你仍然可以使用break提前终止,因此停止项的动态评估比使用更烦人:您真的需要动态评估,使用while () {}(或do {} while ())。

在其他一些语言中,例如Lua,停止条件仅在循环初始化时评估一次。它有助于预测循环行为,并且通常更具性能。

答案 6 :(得分:2)

在x86上,可以在汇编级别完成循环,而无需进行while循环。如果ecx大于0,循环指令会减小寄存器ecx的值并跳转到操作数,否则它什么都不做。这是一个经典的for循环。

mov ecx, 0x00000010h
loop_start:
;loop body
loop loop_start
;end of loop

答案 7 :(得分:2)

在Ada(我相信大多数其他Algol派生语言)中,“for”循环的终止条件仅在循环开始时计算一次。例如,假设您在Ada中有以下代码

q := 10;
for i in 1..q loop
    q := 20;
    --// Do some stuff
end loop;

这个循环将完全迭代10次,因为当循环开始时q为10。但是,如果我在C中编写看似等效的循环:

q = 10;
for (int i=0;i<q;i++) {
   q = 20;
   // Do some stuff
}

然后循环迭代20次,因为在我变得足够重要的时候,q被改为20。

当然,C方式更灵活。然而,这有很多负面影响。显而易见的是,程序必须浪费精力在每个周期重新检查循环条件。一个好的优化器可能足够智能在这样的简单情况下解决问题,但是如果“q”是全局的并且“做一些事情”包括一个过程调用(因此理论上可以修改q)会发生什么?

事实上,我们只知道关于Ada循环的方式,而不是关于C循环。这意味着,在优化器中具有相同级别的智能和工作量,Ada可以在优化方面做得更好。例如,Ada编译器知道它可以用10个内容副本替换整个循环,无论这些内容是什么。 C优化器必须检查和分析内容。

这实际上只是C语法设计困扰编译器的众多方法之一。

答案 8 :(得分:1)

这些语言纯粹主义者似乎永远不会意识到的是C的全部意义,并且在某种程度上,C ++正在提供实现你想要的东西和你想要的东西的可能性。现在可以肯定的是,如果你的结束表达式的结果发生了变化,那么最好只使用while循环。也许问题是程序员得到的印象C实现了“高级别”,但是每个人都应该知道C是一种非常低级的语言。

答案 9 :(得分:1)

  
    

非常“实用性”意味着潜在的CS专业必须学习Kernighan在设计C时的错误,特别是一个臭名昭着的事实,即一个for循环重复评估for条件,这个重复的同时并且不能匹配大多数的行为实现for循环的其他语言。

  

Muaahahahaa!

我喜欢评论者的基本(双关语)断言:

  • Kernighan的错误
  • 臭名昭着的事实
  • 无法匹配大多数其他语言的行为

对我而言,它的气味从未成功掌握C的基本功能/哲学。

简介

当我还在大学学习物理时,我发现当我发现C的 循环时,C(即类C语言)将成为我的首选语言。

显然,一个人的风格失败是另一个人的风格成功。这将结束本次讨论的主观部分。

也许Kernigan的应该被称为循环?

开玩笑吧?也许不是。

该评论的作者显然决定每种语言结构在不同语言中应具有相同的行为。因此,将 重命名为循环可以缓解他/她的不适。

问题在于作者无法理解C的 的扩展,而不是一个不同的循环。这意味着 的语法糖灯版本,而不是 可能包含的其他语言被阉割了。

真的需要C吗?

引用评论作者,他/她对进行以下断言

  • for 用于从开头到结尾的常量循环
  • 用于评估每次迭代条件的循环

当你可以组合它们时,拥有正交特征是一件好事。但在我所知的所有语言中,没有办法将用于同时组合在一起。

示例:如果对于评论者的语言,您需要从开始到结束的循环(如 for ),但仍然能够在每次循环迭代时进行评估(如< strong> while ):您可能需要在容器的子集(而不是整个容器)中查找根据某些状态测试的值?

使用类似C语言的用于。在评论者的理想语言中,你只是,骇人听闻,因为你没有 for_while 循环(或者C for 循环)。

结论

我相信我们可以通过以下声明总结评论家对C(和C语言)的批评“ Kernigan臭名昭着的错误,即不给C语言提供以前的现有语言”,其中可以再次概括为“从不认为不同”。

引用Bjarne Stroustrup:

  
    

只有两种语言:人们抱怨的语言和没人使用的语言。

  

所以,总而言之,评论者的评论应被视为赞美。

^ _ ^

答案 10 :(得分:0)

也许Knuth指的是BASIC。

在BASIC(较旧的,我不了解VB)中,FOR循环的实现方式不同。

E.g。循环十次

FOR N = 1到10

...

NEXT N

----或---- 找到100以下偶数的总和

FOR N = 0到100第2步

...

NEXT N

在C中,您可能在“for”中有任何条件来检查是否退出循环。我认为更灵活。