忘记在做... while 循环

the*_*tar 59 c++

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

int main() {
  int status=1;
  /*do*/ {
    status = foo();
  } while (status);
}
Run Code Online (Sandbox Code Playgroud)

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

据我所知,大括号代码运行{ ... },然后程序检查 while 子句中的条件。

Che*_*yDT 92

我假设你实际上int status 循环体,否则代码不会编译。(甚至没有do到位。)

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

int main () {
  int status;

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

  while (status) {
    // empty loop body
  }
}
Run Code Online (Sandbox Code Playgroud)

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

之所以与while (status);the 相同,while (status) {}是因为您可以放置​​单个语句或块,并且;是一个不执行任何操作的有效语句。

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

  • “*写类似`while (status);`的东西甚至不是无意义的*”它是无意义的,因为如果`status`是真的,那么它就是UB。优化器可能假设“status”为零,删除循环并开始在“foo()”和其余部分的代码上疯狂运行。最好说这样的循环*必须*做一些有意义的事情,或者“status”被标记为“易失性”,或者是一个带有“operator bool”的对象,等等。 (5认同)
  • `operator bool()` 也可能有副作用,所以它不像编写 `while(someVariable);` 需要外部更改(易失/原子)才能退出。并且 `while(someFunction())` 也可以改变 (2认同)
  • 谢谢,这就是我一直在寻找的答案。我经常使用一行 if 语句。但我没有意识到你可以为 while 循环执行此操作,并且还可以省略任何语句。如果我正在设计一种语言,我可能会要求使用一个明确的单词,例如 pass 来指定该语言中的“不执行任何操作”,并且要求无花括号的 while 循环至少有一个语句。 (2认同)
  • 只是为了明确 while 循环的有用性:“while(someVariable)”的用途可能有限,在其他评论中讨论过,“while(f());”确实是一个非常有用的结构。通常我们需要 `f` 来做一些工作,然后如果需要再次运行则返回 true,如果工作完成则返回 false。 (2认同)
  • @CortAmmon我通常会在这样的情况下添加一个显式的“继续”:“while (foo()) continue;”。可以更容易地发现那些实际上是“while”行末尾的无关分号的实例。 (2认同)

Mar*_*tin 14

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

  1. while ( condition ) statement
  2. do statement while ( expression );

statement

  • 一个单一的声明或
  • 花括号中的语句块或
  • 空语句 ( ;)

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

int main () {
  int status;

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

  while (status) /* empty statement */;
}
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,您不是第一个从缩进/换行中得出错误结论的人。


Aco*_*orn 6

为什么这仍然编译和运行?

不是因为status没有定义。

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

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