在for循环末尾加上";"的目的是什么?

75

我找到了以下代码:

int func_prim (int zahl) {
    int count;
    if (zahl < 0)
        return -1;

    for (count = 2; zahl % count != 0 && zahl >= count; count++);
    if (count == zahl)
        return 1;
    return 0;
}

此函数的目的是检查一个数是否为质数。

我不明白为什么for循环末尾要加上;

                                                            v
for (count = 2; zahl % count != 0 && zahl >= count; count++);

没有那个,代码就不能正常工作。

这是什么意思?


48
for循环结尾有一个分号是因为作者很邪恶!在这种情况下,应该将其放在单独的一行上。 - sh1
21
如果作者希望整个循环在一行上,可以使用 { } 来代替 ;,以表明意图是让循环为空,并且不包括后续的任何行。 - Eliah Kagan
22
有时候,当我想强调循环体的空白时,我会写成 for (...) continue; - sh1
8
但是这种使用分号的方法有两个问题:(1)它看起来像一个错误,可能会被一些好心的开发者误删;(2)如果你没有注意到它,你会期望下面的语句会被“循环”。后者是一个特别危险的情况,因为程序员学习了不要特别注意行末的分号;就像读者不太注意一句话是否以句号结尾一样。 - Dawood ibn Kareem
6
@Marco13 为何自动假设原作者无知?你不喜欢这种写法,很好。但是仍有许多人更喜欢使用for循环的方法:毕竟有初始化、停止条件和迭代,这些都可以自然地通过for结构表达出来。虽然你不同意,但我相信作者知道什么是while循环。顺便提一下,这也是为什么团队需要编码规范的原因。 - coredump
显示剩余13条评论
11个回答

90

它的意思与以下完全相同:

for(count = 2; zahl % count != 0 && zahl >= count; count++)
{
}

8
这是一种更好的写空循环体的方法(尽管在单行上写“{}”可能更容易看出它是一个空体)。如果不考虑遗留代码,语言在没有分号空语句规则的情况下会更好。必须在“do...while(...)"后面加上分号也很愚蠢,但至少我曾经看到过一些聪明的用法。 - Marc van Leeuwen
5
我觉得使用 ; 更易读。空代码块看起来像是一个意外,就好像程序员忘记填写代码主体一样。而空语句则能明确表达他的意图。 - Paul
19
@Paulpro,我认为这两种方式都有潜在的错误,而使用for (...) { /* empty block */ }会更清晰,因为它能够表明这是有意为之。 - atk
2
我不是C语言程序员,但为什么这样做会有用呢? - Ethan Bierlein
6
@EthanBierlein 这里的“工作”是由( )中的语句完成的;此循环的目的是使 count 最终具有基于 zahl 的特定值。 - M.M
显示剩余2条评论

53
一个for循环有关键字for,后面跟着括号,其中包含三个可选表达式,用分号隔开,然后是在每次迭代中执行的主体。
你示例中for循环的目标是设置count的值,该值将与zahl进行比较,在随后的if语句中。这是通过分号分隔的表达式实现的,因此循环体不需要执行任何操作。
由于循环不需要执行任何操作,它使用空语句作为其主体。
如果省略末尾的;并且没有进行其他更改,则for循环后的if语句本身将成为for循环的主体。(这不是预期的结果,会破坏程序,正如您所观察到的。)
然而,将循环体仅由一条位于同一行的;组成,并不是编写空循环体的唯一方法,也不是最明智的方法。它可以完美地工作,但问题在于其他读者——也许是相同的程序员,在以后返回该项目时——可能会想知道它是否真的是一个错误。毕竟,在使用C风格语言进行编码时,我们经常在行末键入分号,因此很容易在不应有分号的位置键入额外的分号。
另一个问题是,在一个选用;作为循环体的单行样式的代码中,很难识别何时某人实际上已经犯下了放置不应有的;的错误。
因此,这些替代方案可能更可取:

4
虽然我同意使用分号和大括号,但我认为使用“continue;”会让人感到非常奇怪。它会让我感觉好像有人期望继续执行外层循环或其他操作。这绝对是一种让人摸不着头脑的时刻,而且在编写代码时需要避免这种情况。我想说,最易读的方式是使用大括号来表达意图。 - Angew is no longer proud of SO
5
另一个选择(可与其他选项结合使用)是插入注释:for (...) /* do nothing */ ;,以明确表明这是有意为之。 - user743382
2
我会在备选方案中添加“在循环内增加变量”的方法,例如:for(count=2; zahl % count != 0 && zahl >= count;) ++count; - Massa
3
@Massa - for循环在定义上是一种计数循环;将增量器移出循环并没有给你任何理由使用for而不是while - user719662
@vaxquis,你还有初始化;不管怎样,我一开始就会用 int count = 2; while( zahl % count && zahl >= count ) ++count; :D - Massa

41

在for循环结尾处的分号意味着它没有循环体。如果没有这个分号,C会认为if语句是for循环的循环体。


4
+int(PI/3)的翻译是“将PI除以三后向下取整”。这句话的意思是指精确地计算出PI除以三并向下取整的结果,而不涉及“良好/不良编码实践”的讨论。 - user719662
绝对是 +1,一个干净、简短、客观的答案,不会试图宣称代码质量差或风格不佳。 - Vality
1
for循环肯定有一个主体。只是这个主体完全由空语句“;”组成。孤立的“;”在几乎所有允许语句的地方都是有效的(如果不是实际上),因为孤立的“;”是一个空语句(也是一条语句)。 - user

22

for循环的语法(迭代语句)是:

for ( clause-1 ; expression-2 ; expression-3 ) statement 

statement可以是一个空语句;)。C11 6.8.3说:

一个仅包含分号的空语句不执行任何操作。

在第5段中,它举了一个例子:

在程序片段中

char *s;
/* ... */
while (*s++ != '\0')
    ;

空语句被用于为迭代语句提供一个空的循环体。

在相同的情况下也会发生这种情况。

for (count = 2; zahl % count != 0 && zahl >= count; count++);

; 用于给 for 循环语句提供一个空的循环体。如果没有 ;,则紧跟在 for 循环后面的语句将被视为循环体并执行。


1
+int(PI/3)是精准的,不需要进入“好坏编码实践”的争论。 - user719662
@vaxquis; 是的,因为问题是关于“为什么for循环结尾要有;”而不是“这样做好不好?” - haccks

17

除了其他优秀回答中提到的内容之外,我想指出

for(count=2; zahl % count != 0 && zahl >= count; count++);

(也就是说,使用空语句来递增“计数器”的 for 循环)等价于

count=2;
while(zahl % count != 0  && zahl >= count)
{
    count++;
}

这将使代码的目标比一些列举的替代方案更明确:如果不存在注释(如所示情况),则带有空语句的循环可能会让其他维护或使用代码的程序员感到困惑(就像这里的 OP 一样)。

上下文可以帮助区分语句的真实范围,但相比于带有空语句的 for 循环,带有语句的 while 循环需要更少的工作来理解其范围。


1
小问题:这并不严格等价,因为在第二个版本中,“count”是在更大的范围内声明的。 - coredump
我的意思是,一般来说... 这里 count 在两个版本的函数开头都被声明了。 - coredump
2
@coredump:考虑到这个循环的目的——在这种特定情况下——是为了使count达到正确的值,以便它可以进一步使用,因此count必须存在于更大的范围内才能正常工作,因此它完全等效。 (尽管这仍然是对for循环的丑陋滥用,但Federico是正确的,这是表达所涉及的语义的正确方式。) - Mason Wheeler
@MasonWheeler 是的,我在考虑一般情况。在这里两者确实是等价的。 - coredump
1
为什么for循环不清晰?我理解for循环明确地包含了初始化、条件和迭代语句,这三者被逻辑地捆绑在一起。虽然使用for循环并非必须,但在某些情况下它看起来更加合理和清晰。 - Vality
如果这个回答能解释为什么所做的声明成立,以及为什么这回答了提问者的问题,那就更好了。是的,你可以将一个重写为另一个而几乎没有负面影响,但我不明白说明这一点如何帮助提问者理解 for(...); 行末的分号在做什么。 - user

12

for语句:

for语句是一个循环语句,其结构允许易于进行变量初始化、表达式测试和变量修改。它非常方便用于制作计数器控制的循环。下面是for语句的一般形式:

 for (initialize; test; step)
   statement

空语句:

空语句仅由一个分号构成。

 ;

空语句并不执行任何操作。它不会在任何地方存储值,也不会在程序执行期间增加时间。

通常情况下,空语句用作循环语句的主体,或者在for语句中作为一个或多个表达式使用。以下是一个使用空语句作为循环主体的for语句示例(仅供娱乐,同时计算n的整数平方根):

 for (i = 1; i*i < n; i++)
   ;

这里是另一个示例,它使用空语句作为for循环的主体,并且还产生输出:

 for (x = 1; x <= 5; printf ("x is now %d\n", x), x++)
   ;

有时候,空语句也会被用来跟随一个本应该是块中最后一件事情的标签。

在您的情况下,;Null Statement of the for 语句

int func_prim (int zahl) {
  int count;
  if (zahl < 0)
    return -1;

  for (count = 2; zahl % count != 0 && zahl >= count; count++)
    ;
  if (count == zahl)
    return 1;
  return 0;
}

没有它,if 就变成了 for 语句:

int func_prim (int zahl) {
  int count;
  if (zahl < 0)
    return -1;

  for (count = 2; zahl % count != 0 && zahl >= count; count++)
    if (count == zahl)
      return 1;
  return 0;
}

因此,行为方式不同。


1
很好地引用自文档。+1 - Vality

12

for 循环后面的 ; 只是表示该 for 循环不会做任何事情,除了增加计数器 count


10
< p > for循环仅用于增加count的值。 < /p >

9

通常,for循环会有一个循环体,

循环体用大括号{ }括起来。

然而,对于单个语句的循环体,大括号是可选的。

;是一个空语句。

结合上述内容,for循环一直执行,直到条件变为false为止。


6

for循环基本上遍历所有小于或等于zahl但大于2的数字,并将其存储在变量count中。当它遍历所有这些数字时,它会检查zahl是否可被count整除。如果zahl能够被count整除,则停用循环。否则,当count等于zahl时停止循环。

for循环后的if语句检查count是否等于zahl。如果是,则说明循环遍历了所有小于zahl而大于2的数字。这意味着zahl可被所有小于自身且大于2的数字整除,使zahl成为质数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接