Use of null statement in C

27
典型的使用场景有哪些呢?
;

在C语言中?
我知道它基本上是用来跳过编译器期望的表达式,但我只对这种用法的真实世界示例感兴趣。

9
如果你需要一个空语句,我更倾向于使用“{}”,因为这样看起来不太可能是意外的。所以说,“从不”;-) - Steve Jessop
1
{} 在某些情况下并不实用,而分号 ; 则可能更为实用。 - R.. GitHub STOP HELPING ICE
@JanSchultke 问题的范围有些不同。你提出的问题是关于为什么C/C++编译器在遇到空语句时不会给出编译时错误,也就是关于C/C++语言设计背后的原因。而我的问题是关于利用这种语言结构的最佳实际用例。 - undefined
重复目标是一个[语言设计]问题,语言为什么允许这样的问题的答案在于使用案例。这些问题的范围略有不同,但一个问题的答案可能会回答另一个问题,这是判断是否为重复的标准。 - undefined
@JanSchultke 使用语言设计师的用例可能与C/C++编译器用户的用例不重叠。所以,我仍然认为这些是不同的问题,尽管我可能同意部分用例是相匹配的。 - undefined
显示剩余2条评论
13个回答

25

这通常是被预处理器剥离的代码块的副作用,例如:

#if DEBUG
    #define ASSERT(_x) Assert(_x)
#else
    #define ASSERT(_x)
#endif


ASSERT(test);    // Results in null statement in non-debug builds
那么,或者在循环中,当你的条件已经包含每次迭代需要执行的内容时。

示例中有一个拼写错误:Assert(x) 应该替换为 Assert(_x),因为 _x 已经被用作参数名称(我无法编辑答案,因为编辑必须至少包含6个字符)。 - vinc17
另外,对于#else情况,我更愿意使用#define ASSERT(_x) ((void) 0),这样当一个人忘记了分号时就会报错,例如:ASSERT(test1) ASSERT(test2);但在这种情况下,不再有空语句。 - vinc17

18
while (*(dst++) = *(src++))
    ;

16

当在函数末尾(或更准确地说,在任何块的末尾)放置一个标签,例如:

void foo(void)
{
    // ...

exit:
    ;
}

7
我会将 return; 完全替换进去。 - Steve Jessop
14
@steve 标签可以放置在任何代码块的结尾,因此return并不总是适用的。例如,当您想要从内层循环中继续外部循环时,就会出现多层嵌套循环(当然,您也可以使用 label: continue; } 而不是 label: ;})。 - Jim Balter
@Jim: 或者你可以写 continue; 而不是 goto label;。我相信有这样的用途,甚至可能比“我去到某个宏定义的标签,所以在一些特殊情况下无法替换 continue”更少疯狂的用途。只是我想我从未见过这样的情况。 - Steve Jessop
1
@Steve 请再仔细阅读我写的内容:“从内部循环中继续外部循环” - 只写 continue; 将会继续错误的循环。Bliss 语言没有 goto,但是有用于 break/continue 外部循环的循环标签;太遗憾了 C 没有这个功能。 - Jim Balter
1
@Jim:好的,我明白了。是的,继续外部for循环需要一个空语句。循环标签很少会被忽略,但在你确实需要它们的时候,它们的缺失尤其令人恼火,因为这会导致愚蠢的争论,是否应该重构成额外的函数来避免使用“goto”。 - Steve Jessop

5
while (somethingWithSideEffects()) ;

4

我曾经在可能是不寻常的情况下(而且某些/许多人可能会觉得这是错误的)使用过它。有时候我需要编写非常复杂的 if 条件语句,但没有 else 子句,而且必须对 if 条件进行取反。显然它可以像这样:

if ( !( overly complex condition ) )
  {
  do stuff
  }

对我来说,有时候用正逻辑思考可能更加合理。换句话说,如果过于复杂的条件成立,我不希望代码运行。因此,我改为以下写法:

if ( overly complex condition )
  ;  // do nothing
else
  {
  do stuff
  }  

1
我发现最好将其分解成多个 if。如果删除 !( ),它仍然会很复杂。 - ikegami

2

例子:

 while (!kbhit())
     ;

应该是不言自明的。

2

有一个有点不寻常的用法——但我真的很欣赏空语句的存在——就是当我有两个条件和两个动作时,我发现我可以最自然地这样表达:

if(condition1)
     /* do nothing */ ;                                                      
else if(condition2)
     do_something;                                                           
else do_something_else;                                                      

通常情况下,condition1测试一切是否正常,但如果不是,则condition2区分两种不同的异常操作:do_somethingdo_something_else
当然,这不是表达这种情况的唯一方式。也可以重复使用condition1
if(!condition1 && condition2)
    do_something;                                                           
else if(!condition1)
    do_something_else;

但这似乎不够优雅,因为它重复了condition1。或者可以使用嵌套的if语句:

if(!condition1) {
    if(condition2)
         do_something;                                                   
    else do_something_else;                                              
}

但是当然,嵌套的if语句也很容易过于复杂和晦涩难懂。因此,我通常更喜欢第一种版本,使用null语句。


2

一款符合标准的编译器的单元测试。


2

我可以想到的是对 scanf 的验证。当用户没有提供正确的输入时,scanf 会卡住。因此,为了防止 scanf 被卡住,必须删除直到行尾的字符。

if( scanf("%d",&integer) == 0 )
{
    while( getchar() != '\n' ) ;
    // ....
}

1
我能想到的唯一用途是:
1- 在循环结束时,操作已经编码在循环语句中。例如:while(a[i--]); 2- 在标签结尾处,不需要执行任何操作。例如:Label: ;

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