忘记在 do...while 循环中做

Posted

技术标签:

【中文标题】忘记在 do...while 循环中做【英文标题】:Forgot do in do... while loop 【发布时间】:2020-08-02 22:00:41 【问题描述】:

我有一个烦人的错误,我忘记在 do ... while 循环中写入 do

int main() 
  int status=1;
  /*do*/ 
    status = foo();
   while (status);

为什么它仍然可以编译和运行?在我看来,编译器应该拒绝这是无意义的,或者至少发出警告(我的编译器选项中有-Wall)。我正在使用 C++11。

据我所知,大括号代码运行 ... ,然后程序检查 while 子句中的条件,并无限期地进行。

【问题讨论】:

您自己的解释似乎是正确的。 编译时出现错误。它抱怨没有声明变量status 你写的是一个大括号块,后面跟着一个空主体的while循环。 虽然没有警告,因为这可能是完全有效的代码,我很确定像 clang-tidy 这样的静态代码分析器会警告你检查的变量没有在循环中更新身体。另请注意,-Wall 到目前为止并非启用所有警告,即使它的名称很不幸也是如此。 -Wall -Wextra 的组合是一个很好的基础,也许还有更适合你的问题的警告 I ran into a "while-while" loop 最近这样。诊断是一样的。 【参考方案1】:

我假设您实际上在循环体之外有int status,否则代码不会编译。 (甚至 do 也没有。)

修复后,您编写的代码在没有do 的情况下仍然有效,但正如您已经正确指出的那样,它做了一些不同的事情。让我稍微重写一下以显示它是如何解释的:

int main () 
  int status;

   // anonymous braced block that just creates a new scope
    status = foo();
  

  while (status) 
    // empty loop body
  

像does have its uses 这样的独立块,例如使用RAII - 它可以包含一个带有对象的局部变量,该对象的析构函数在超出范围时释放一些资源(例如文件句柄),除其他外。

while (status);while (status) 相同的原因是因为您可以放置​​单个语句或块,而; 是一个不执行任何操作的有效语句。

while (someVariable); 之类的东西一般来说甚至都不是荒谬的(尽管在这种情况下当然是这样),因为它本质上是一个自旋锁,一种忙等待的形式——如果另一个它会离开循环处理器内核、某些 I/O 组件或中断会修改someVariable 的值,从而不再满足条件,并且它会立即执行此操作。您可能不会在“占用 CPU”是一件坏事的桌面平台上编写此类代码(内核模式代码中的特定场景除外),而是在像微控制器这样的嵌入式设备上(您的代码是唯一运行的代码) ) 它可以是实现等待某些外部更改的代码的一种完全有效的方式。正如 Acorn 在 cmets 中指出的那样,这当然只有在 someVariablevolatile(或其他不可预测的情况)时才有意义,但我说的是一般变量上的繁忙循环。

【讨论】:

"写像while (status); 这样的东西甚至不是荒谬的" 写出来的东西是荒谬的,因为如果status 是真的,那么它就是UB。优化器可能假设status 为零,删除循环并开始在foo() 和其余部分的代码上狂奔。最好说这样的循环必须做一些有意义的事情,或者status被标记为volatile,或者是一个带有operator bool的对象,等等。 operator bool() 也可能有副作用,所以写while(someVariable); 并不需要外部更改(易失性/原子)才能退出。 while(someFunction()) 也可以改变 谢谢,这是我正在寻找的答案。我经常有单行 if 语句。但是我没有意识到您可以在 while 循环中做到这一点,并且还可以省略任何语句。如果我正在设计一种语言,我可能会要求一个明确的词,比如 pass 来指定语言中的“什么都不做”,并要求无支撑的 while 循环至少有一个语句。 只是为了明确说明while循环的用处:while(someVariable) 可能用途有限,在其他 cmets 中讨论过,while(f()); 确实是一个非常有用的结构。很多时候我们需要f做一些工作,如果需要再次运行则返回true,如果工作完成则返回false。 @CortAmmon 我通常会在这种情况下添加明确的continuewhile (foo()) continue;。更容易发现那些在while 行末尾实际上是无关分号的实例。【参考方案2】:

编译器无法在此处引发错误,因为根据 C++11 标准的第 6.5 节,这是完全有效的代码。其实while有两种口味:

    while ( condition ) statement do statement while ( expression );

statement可以

单个语句或 花括号中的语句块或 空语句 (;)

考虑到这一点,让我格式化您的代码,编译器如何看待它:

int main () 
  int status;

   // braced block that just creates a new scope
    status = foo();
  

  while (status) /* empty statement */;

虽然对于人类读者来说可能很明显,您打算在花括号中循环代码,但这对编译器来说并不明显。这与 C++ 编译器通常不考虑缩进和换行这一事实有关。将它们考虑在内的分析工具可能会警告您,您格式化代码的方式与它实际在做什么并不一致,并为您纠正。这会让你的错误更加明显。或者也许有一天我们会得到一种语言特性,它允许我们明确地说“空语句”。这将使我们能够清楚地说明我们的意图。一旦我们有了编译器,当代码不清晰时,编译器就会发出警告。在那之前我们必须小心——C++ 是一种强大的语言,但它也有一些锋利的边缘......

顺便说一句,你是 not the first one,他从缩进/换行中得出了错误的结论。

【讨论】:

【参考方案3】:

为什么它仍然可以编译和运行?

不是因为status 没有定义。

在我看来,编译器应该拒绝这是无意义的,或者至少发出警告(我的编译器选项中有 -Wall)。

假设你定义了status,它是一个有效的程序。一些编译器或分析器可能会为无限循环或无操作 while 主体生成警告。

【讨论】:

以上是关于忘记在 do...while 循环中做的主要内容,如果未能解决你的问题,请参考以下文章

JS中 do while循环问题

C语言 do while 和 while 循环

循环(while,do...while)

00013_循环语句do...while

Java 循环结构 - for, while 及 do...while

do-while循环结构