繁体   English   中英

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

[英]C for loop implemented differently than other languages?

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

“非常'实用'的意思是,可能的CS专业人士必须学习Kernighan在设计C时的错误,尤其是臭名昭著的事实,即for循环反复评估for条件,该过程重复出现而与大多数其他语言的行为不匹配它实现了for循环。”

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

这个家伙在说什么? 您如何实现一个for循环,而不仅仅是一个while循环的语法糖?

考虑一下:

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

他可能指的是for循环,例如for i:=0 to N以及循环访问集合元素的for-each循环。 我怀疑所有具有C样式for循环的语言实际上都是从C那里得到的。

马格努斯说的没错,但还应该注意的是,在大多数语言中(C前),条件是结束条件(即“当我等于100时停止”)。 在C语言(和大多数后C语言)中,这是继续条件(即,“当我小于100时继续”)。

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

举个例子

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

我知道您通常会做x = 0; x <= 10; ++x x = 0; x <= 10; ++x 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"

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

基本上,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,则跳转到操作数,否则不执行任何操作。 这是经典的for循环。

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

在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)会发生什么?

在铁的事实是,我们刚认识的方式更多关于阿达环比我们做的关于C环。 这意味着,在优化器中具有相同水平的智能和精力,Ada可以做得更好。 例如,Ada编译器知道可以用10个内容副本替换整个循环,无论这些内容是什么。 AC优化器必须检查和分析内容。

实际上,这只是C语法设计阻碍编译器的众多方式之一。

这些语言纯粹主义者似乎从未意识到的是C的全部要点,并且在一定程度上是C ++,这使实现所需的内容和所需的方式成为可能。 现在要承认的是,如果结束表达式的结果发生变化,最好使用while循环。 也许问题在于程序员对C实现了“高级”印象,但是每个人都应该知道C是一种相当低级的语言。

非常“实用”的意思是,可能的CS专业人士必须学习Kernighan在设计C时的错误,尤其是臭名昭著的事实,即for循环反复评估for条件,这种情况重复了一段时间,却无法匹配大多数其他语言的行为,实现一个for循环。

啊哈哈哈哈!

我喜欢审阅者的基本(双关语)断言:

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

对我来说,这是一个从未成功掌握C的基本功能/哲学的人的味道。

介绍

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

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

也许Kernigan的for应该被称为loop?

开玩笑? 也许不是。

该评论的作者显然认为每种语言构造在所有语言中都应具有相同的行为。 因此, as 循环重命名将减轻他/她的不适感。

问题在于作者无法理解C的forwhile的扩展, 不是不同的循环。 这意味着, 虽然一个语法糖简易版本,而不是在那里可能已被阉割下来,而其他语言。

真的需要C吗?

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

  • for是从头到尾的恒定循环
  • while是for循环用于在每次迭代时评估条件

可以将正交特征组合在一起是一件好事。 但是我所知道的所有语言都无法将forwhile结合在一起。

示例:如果对于审阅者的语言,您需要一个从头到尾的循环(例如for ),但仍然能够在每次循环迭代中求值(例如while ):您可能需要在一个子集中找到一个容器(不是整个容器)的值,根据某些状态测试得出的值?

在类似C的语言中,您可以使用for 用审阅者的理想语言,您会很好地破解一些丑陋的代码,因为您没有for_while循环(或C的for循环)而哭泣。

结论

我相信我们可以用以下陈述“ Kernigan的臭名昭著的错误,就是不给C使用以前存在的语言的语法 ”来总结审阅者对C(以及类似C的语言)的批评,这可以再次概括为“ 从不以为然 ” 。

引用Bjarne Stroustrup:

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

因此,总而言之,审稿人的评论应被视为赞扬。

^ _ ^

也许Knuth指的是BASIC。

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

例如循环十次

对于N = 1到10

...

下一个N

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

对于N = 0到100步骤2

...

下一个N

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM