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



我在Knuth的"计算机编程的艺术"的评论中读到了以下内容:

"非常'实用性'意味着未来的CS专业必须学习Kernighan在设计C时的错误,特别是一个臭名昭著的事实,即for循环反复评估for条件,它重复while并且无法匹配大多数实现for循环的其他语言的行为。

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

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

考虑一下:

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) // ...

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

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

会没事的,编译器可以优化它。

如果您在 for 语句的继续部分中使用函数,例如

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 循环的所有元素组合在一起,以便您可以更轻松地理解它。

他可能指的是像 for i:=0 to N 这样的循环和迭代集合元素的 for-each 循环。我怀疑所有具有 C 样式 for 循环的语言实际上都是从 C 中获取的。

Magnus 说得对,但也应该注意,在大多数语言(pre-C(中,条件是结束标准(即"当我等于 100 时停止"(。 在 C(以及大多数后 C 语言(中,它是继续标准(即"在 i 小于 100 时继续"(。

基本上,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,停止条件只在循环初始化时计算一次。它有助于预测循环行为,并且通常性能更高。

在 x86 上,for 循环可以在程序集级别完成,而无需进行 while 循环。循环指令减小寄存器 ecx 的值,并在 ecx 大于 0 时跳转到操作数,否则它不执行任何操作。这是一个经典的循环。

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

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

以这个例子为例

int x;
for (x = 10; x != 0; --x)
{
    printf ("Hellon");
}

我知道你通常会做x = 0; x <= 10; ++x,但一切都会在大会上揭示。

一些伪程序集:

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

在这个例子中,我们不断跳回循环以打印"hello"10次。但是,我们每次循环都使用jne指令评估条件。

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

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

我们不需要任何其他指令,所以它更快。这只是一个简单的例子 - 我会尝试找到一个更好的!

在 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 语法设计阻碍编译器的众多方式之一。

这些语言纯粹主义者似乎从未意识到的是 C 的全部意义,在某种程度上C++,是提供了实现你想要的东西和你想要的方式的可能性。现在诚然,如果结束表达式的结果发生变化,最好只使用 while 循环。也许问题是程序员得到的印象是C实现了"高级",但每个人都应该知道C是一种相当低级的语言。

非常"实用性"意味着未来的CS专业必须学习Kernighan在设计C时的错误,特别是一个臭名昭著的事实,即for循环反复评估for条件,它重复while并且无法匹配大多数实现for循环的其他语言的行为。

呜哈!

我喜欢审稿人的基本(双关语(断言:

  • 克尼根的错误
  • 臭名昭著的事实
  • 无法匹配大多数其他语言的行为

对我来说,这闻起来像是一个从未成功掌握C的基本特征/哲学的人。

介绍

当我

还在大学学习物理时,当我发现C的for loop时,我发现C(即类C语言(成为我选择的语言。

所以显然,一个人的风格失败就是另一个人的风格成功。讨论的主观部分到此结束。

也许克尼根的 for 应该被称为循环?

开玩笑?也许不是。

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

问题是作者无法理解C的forwhile的扩展,而不是不同的循环。这意味着 while 是 for 的句法糖轻版本,而不是 while 其他语言,其中 for 可能已被阉割。

真的需要C吗?

引用评论的作者的话,他/她对 forwhile 做出了以下断言:

  • for 用于常量循环,从开始到结束
  • while for 循环在每次迭代时评估条件

当您可以将正交要素组合在一起时,具有正交要素是一件好事。但是在我所知道的所有语言中,没有办法将 forwhile 结合在一起。

示例:如果对于审阅者的语言,您需要一个从开始到结束的循环(如 for(,但仍然能够在每次循环迭代(如一段时间(进行计算:您可能需要在容器的子集(而不是整个容器(中找到一个值根据某个有状态测试?

在类似 C 的语言中,您使用 for。在审稿人的理想语言中,你只是,好吧,破解一些丑陋的代码,哭泣,因为你没有for_while循环(或C的循环(。

结论

我相信我们可以用以下陈述来总结审稿人对C(和类C语言(的批评">Kernigan臭名昭著的错误,即没有给C提供以前现有语言的语法",这可以再次总结为">永远不要思考不同"。

引用Bjarne Stroustrup的话:

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

因此,总而言之

,评论者的评论应被视为一种赞美。

^_^

也许高德纳指的是BASIC。

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

例如 循环十次

对于 N=1 到 10

下一个 N

----或----查找小于 100 的偶数之和

对于 N=0 到 100 步骤 2

下一个 N

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

相关内容

  • 没有找到相关文章

最新更新