忘记在 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 中指出的那样,这当然只有在 someVariable
是 volatile
(或其他不可预测的情况)时才有意义,但我说的是一般变量上的繁忙循环。
【讨论】:
"写像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 我通常会在这种情况下添加明确的continue
:while (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 循环中做的主要内容,如果未能解决你的问题,请参考以下文章