if-else块中的“ if(0)”块的用途是什么?


141

我的问题是我在本主题中提到的那行,在生产代码中的许多地方都可以看到。

整体代码如下所示:

if (0) {
    // Empty braces
} else if (some_fn_call()) {
    // actual code
} else if (some_other_fn_call()) {
    // another actual code
    ...
} else {
    // default case
}

其他分支与我的问题无关。我想知道放在if (0)这里的意思是什么。花括号是空的,所以我不认为它应该注释一些代码块。它会迫使编译器进行一些优化,还是其意图有所不同?

我试图在SO和Internet上搜索这种明显的情况,但是没有成功。关于JavaScript,也有类似的问题,但不是C。还有另一个问题,如果在“ if”条件中分配零会发生什么?,但它讨论了变量的零赋值,而不是'if(0)'用法本身。


2
那句话似乎无关紧要。生成带有或不带有该语句的汇编代码,您将了解实际情况。
鹰头

2
这可能是自动生成的代码。
怪胎

Answers:


91

有时我将其用于对称性,这样我就可以else if{与编辑器自由移动其他对象,而不必介意第一个if

语义上

if (0) {
    // Empty braces
} else 

part不会执行任何操作,您可以依靠优化程序将其删除。


239
个人意见:尽管这可能是按原样编写代码的原因,但我认为这是一个不好的理由。读取代码的频率要高于编写代码的频率,而这种不必要的代码只会增加读取器的解析开销。
user694733 '18

13
@ user694733:您可能会争辩说,if else所有重要代码路径的公共前缀都很好地排列了条件,并使扫描它们更容易。(不过,这是主观的,并且将取决于条件和代码块中的实际内容。)
M Oehm

72
我认为不会if (0) {..}引入任何可解析性/可读性问题。对于任何懂C的人来说,这应该是显而易见的。这不是问题。问题是阅读后的后续问题:“那到底是什么?” 除非是出于调试/临时目的(即,打算在以后“启用” if),否则我建议完全删除。基本上,“读”这样的代码可能没有充分的理由对读者造成不必要的“暂停”。这就是删除它的足够了。
PP

77
似乎肯定会损害可读性。真是太糟糕了,以至于程序员不得不向SO询问它的用途。这不是一个好兆头。
Vectorjohn

26
即使使用此模式,我也不知道您是否可以“ else if无忧无虑地在编辑器中移动”,因为条件可能不是互斥的,在这种情况下顺序很重要。就我个人而言,我只会使用if,并执行早期返回,如有必要,将逻辑链提取到一个单独的函数中。
约翰·吴

105

如果有#if语句,这会很有用

   if (0)
   {
       // Empty block
   }
#if TEST1_ENABLED
   else if (test1())
   {
      action1();
   }
#endif
#if TEST2_ENABLED
   else if (test2())
   {
      action2();
   }
#endif

等等

在这种情况下,可以#if删除所有(和所有)测试,并且代码可以正确编译。几乎所有的编译器都将删除该if (0) {}部分。一个简单的自动生成器可以生成这样的代码,因为它更容易编写代码-不必单独考虑第一个启用的块。


5
在许多情况下,if/ else if链并不是用作决策树,而是用作“对第一个匹配条件的操作”构造,在这种情况下,碰巧具有最高优先级的条件并不是特别的“特殊”条件。虽然我不认为if(0)这是允许所有真实分支具有一致语法的一种方式,但我喜欢它所促进的一致语法。
超级猫

1
在这种情况下,它甚至没有用,因为您可以在没有以下条件的情况下达到相同的效果:只需将else if行分成两部分,然后在中间放置预处理器即可。
康拉德·鲁道夫

1
@KonradRudolph我没有关注;你会怎么写?
JiK

1
@JiK我将删除if (0)分支,然后重新格式化其余分支,使其位于单独else的行上,并沿的行被一个警卫包围#if TEST1_ENABLED && TEST2_ENABLED
康拉德·鲁道夫

5
我想,@ KonradRudolph很好,如果您想将警卫人数增加一倍,并将警卫条件数目增加三倍。
hobbs

44

我已经看到了在生成的代码中使用的类似模式。例如,在SQL中,我已经看到库发出以下where子句。

where 1 = 1

据推测,这可以使添加其他条件变得更加容易,因为所有其他条件都可以作为前缀,and而不是进行其他检查以查看它是否是第一个条件。


4
1=1因为你可以随时添加的也是“有用” where面前,无条件地。否则,您必须检查它是否为空,如果是这样,请避免生成该where子句。
Bakuriu

2
此外,大多数数据库会自动1=1从中“删除”数据库WHERE,因此不会影响性能。
基金莫妮卡的诉讼

7
这在可以自动生成SQL查询的库中是可以接受的,即使DevOps团队也从未发现过。在必须多次读写的高级代码中,这是“不可接受的”。
phagio '18

当生成某种未知最终条件数量的动态SQL时,这确实是一种方便的方法。
船长

1
@freakish确实与我相反:在生成的代码中,可读性差的语法是可以接受的,因为它很可能永远不会被读取,而不是在开发人员维护的高级功能代码中。
phagio '18

44

如所写,该if (0) {}子句编译为空。

我怀疑此梯形图顶部的子句的功能是提供一个方便的位置,通过将或更改为0来一次暂时禁用所有其他功能(出于调试或比较目的)。1true


2
搞定了。除了调试,我看不到其他任何原因。
tfont

16

我不确定是否有任何优化,但是我有两分钱:

发生这种情况的原因是一些代码修改,其中删除了一个主要条件(例如,在初始if块中调用了函数),但是开发人员/维护人员

因此if,他们只是删除了条件if(0)并继续前进,而不是删除关联的块。


3
也不if(0)减少分支机构的覆盖范围吗?
David Szalai

1
@DavidSzalai不是完全-最多减少1(从前2个减少)-但据我所知,仍然需要一击才能覆盖。
Sourav Ghosh

15

这是代码腐烂。

在某些时候,“如果”做了一些有用的事情,情况就发生了变化,也许正在评估的变量被删除了。

修复/更改系统的人员所做的尽可能少的事情影响了系统的逻辑,因此他只是确保代码可以重新编译。所以他留下了一个“ if(0)”,因为这很容易,而且他也不完全确定那是他想要做的。他使系统正常工作,并且没有回去完全修复它。

然后,下一个开发人员出现,并认为这样做是有意的,并且只注释掉了该部分代码(因为无论如何都没有对其进行评估),然后下次触摸该代码时,这些注释将被删除。


2
对。对于古老的代码,请一次删除一个死代码。我无法指望我对“死”代码进行了刀耕火种的次数,却发现刀耕火种遗漏了一些奇怪的副作用。
朱莉在奥斯丁,

15

尚未提及的一种可能性:if (0) {生产线可能会为断点提供一个方便的位置。

调试通常是在未优化的代码上完成的,因此将始终存在错误测试,并能够在其上设置断点。当编译用于生产时,代码行将被优化。似乎无用的产品线提供了用于开发和测试版本的功能,而不会影响发行版本。

上面还有其他好的建议;真正了解目的的唯一方法是追踪作者并进行询问。您的源代码控制系统可能会帮助您。(寻找blame-type功能。)


9

我已经看到了使用模板语言生成的预扩展JavaScript中无法访问的代码块。

例如,您正在读取的代码可能是从服务器粘贴的,该服务器预先评估了当时仅依赖于服务器端可用变量的第一个条件。

if ( ${requestIsNotHttps} ){ ... }else if( ...

曾经预编译过的代码:

if ( 0 ){ ... }else if ( ...

希望这可以帮助您使亲回收编码器时代潜在的键盘活动降低,我对此表现出热情!


1
我同意,在无处不在的自动化时代,我们应该更多地依赖自动生成的代码,因为它使我们可以将更多的时间花在实际的事情上。但就目前而言,我的确切兴趣是如何在引擎盖下构建所有内容。
Zzaponka '18

8

该构造还可以在C中用于实现类型安全的泛型编程,具体取决于编译器仍会检查无法访问的代码的事实:

// this is a generic unsafe function, that will call fun(arg) at a later time
void defer(void *fun, void *arg);

// this is a macro that makes it safer, by checking the argument
// matches the function signature
#define DEFER(f, arg) \
   if(0) f(arg); \              // never actually called, but compile-time checked
   else defer(f, (void *)arg);  // do the unsafe call after safety check

void myfunction(int *p);

DEFER(myfunction, 42);     // compile error
int *b;
DEFER(myfunction, b);      // compiles OK

6

我认为这只是错误的代码。在Compiler Explorer中写一个简单的例子,我们看到在gcc和clang中if (0),即使完全禁用优化,也不会为该块生成代码:

https://godbolt.org/z/PETIks

if (0)尝试消除原因不会更改生成的代码,因此我得出结论,这不是一种优化。

可能在顶部的if块中曾经有一些东西后来被删除了。简而言之,看起来删除它会导致生成完全相同的代码,因此可以随意执行此操作。


6

如前所述,零的计算结果为false,分支可能会被编译器优化。

我之前在代码中也看到了这一点,在代码中添加了一个新功能,并且需要一个kill开关(如果该功能出了问题,您可以将其关闭),再过一段时间后,删除kill开关。程序员也没有删除分支,例如

if (feature_a_active()) {
    use_feature_a();
} else if (some_fn()) {
   ...

成为

if (0) {
   // empty
} else if (some_fn()) {
   ...


1
    Actually according to my opinion, if we put any variable for checking inside
    e.g:-
public static void main(string args[])
{
        var status;
        var empList=_unitofWork.EmpRepository.Get(con=>con.isRetired==true);
        //some code logic 
        if(empList.count>0)
        {
          status=true;
        }
        if(status)
        {
         //do something
        }
        else
        {
        //do something else
        }
}
     if then its dynamically get the value in run time and invoke the logic inside it, else its simply extra line of code i guess.

    Anybody have any depth knowledge why this thing is used....or agree with me.
    kindly respond. 

1

@PSkocik的答案很好,但是我加了两美分。不确定我应该作为评论还是作为回答;选择后者,是因为恕我直言值得其他人看到,而评论则经常是看不见的。

我不仅偶尔使用

if(0) {
   //deliberately left empty
} else if( cond1 ) {
   //deliberately left empty
} else if( cond2 ) {
   //deliberately left empty
...
} else {
   // no conditions matched
}

但是我偶尔也会

if( 1 
    && cond1 
    && cond2
    ...
    && condN
) {

要么

if( 0 
    || cond1 
    || cond2
    ...
    || condN
) {

对于复杂的条件。出于相同的原因-易于编辑,#ifdef等。

为此,我将在Perl中

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • 请注意列表末尾的逗号。我忘记了逗号在C和C ++列表中是分隔符还是分隔符。恕我直言,这是我们了解到的一件事:[ Perl中的逗号结尾是不好的做法吗?逗号]是一件好事。像任何新的符号一样,它需要一段时间才能习惯。

我将if(0)代码与Lisp 进行了比较

(cond   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn))

您猜对了,我可能会缩进为

(cond   
   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn)
)

有时,我试图想象一种更具人类可读性的语法是什么样的。

也许

IF
:: cond1 THEN code1
:: cond2 THEN code2
...
:: condN THEN codeN
FI

受Dikstra的[ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if][Guarded Command Language]的启发。

但是这种语法意味着条件是并行评估的,而if...else-if意味着条件的顺序评估和优先评估。

在编写会生成其他程序的程序时,我开始做这种事情,这特别方便。

在此过程中,当使用英特尔旧的iHDL编写RTL时,我已经编写了类似

   IF 0 THEN /*nothing*/
   **FORC i FROM 1 TO 10 DOC** 
   ELSE IF signal%i% THEN    
      // stuff to do if signal%i% is active
   **ENDC** 
   ELSE   
      // nothing matched 
   ENDIF

其中的FORC..DOC..ENDC是宏预处理器循环构造,扩展为

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      // stuff to do if signal1 is active
   ELSE IF signal2 THEN    
      // stuff to do if signal2 is active
   ...
   ELSE IF signal100 THEN    
      // stuff to do if signal100 is active
   ELSE   
      // nothing matched 
   ENDIF

这是单一分配的非强制性代码,因此,如果您需要执行诸如查找第一个设置位之类的操作,则不允许设置状态变量。

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      found := 1
   ELSE IF signal2 THEN    
      found := 2
   ...
   ELSE IF signal100 THEN    
      found := 100
   ELSE   
      // nothing matched 
   ENDIF

想一想,这可能是我第一次遇到这样的结构。

顺便说一句,某些人对if(0)风格的异议-else-if条件是顺序依赖的并且不能随意重新排序-不适用于RTL中的AND和OR和XOR逻辑-但适用于short-电路&&和||。


-1

我已经看到了它用来处理错误,例如

if(0){
lable1:
   //do something
}
if(0){
lable2:
   //do something
}
.
.
and so on.

if(condition_fails)
   goto lable1;

当使用goto来管理错误时,这将很有帮助,仅当发生错误时才执行语句。我在非常古老的C代码中看到了这一点(其中函数参数写在'()'之外),不要以为现在有人遵循。


-2

我已经看过几次了,我认为最可能的原因是它正在评估旧版本/不同版本的代码/分支中的某些内容,或者可能是为了进行调试,将其更改if(0)为一种删除其中所有内容的懒惰方式。 。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.