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

我的问题是关于我在这个主题中提到的那行代码,我可以在生产代码的很多地方看到它。

总体代码如下:

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 和互联网上搜索这个明确的案例,但是没有成功。关于 JavaScript 也有类似的问题,但不是 C。还有一个问题,当一个零被赋予一个“如果”条件时会发生什么?,但是它讨论了对变量的零赋值,而不是“ if (0)”用法本身。

16558 次浏览

我有时使用这个对称性,所以我可以移动其他 else if{随着我的编辑器,而不必介意第一个 if

从语义上讲

if (0) {
// Empty braces
} else

Part 不做任何事情,你可以指望优化器来删除它。

我觉得只是代码不好。在 Compiler Explorer 中编写一个快速示例,我们可以看到在 gcc 和 clang 中,没有为 if (0)块生成任何代码,即使完全禁用了优化:

Https://godbolt.org/z/petiks

随意删除 if (0)不会对生成的代码产生任何更改,因此我认为这不是一种优化。

有可能在 if的顶部块中曾经有一些东西后来被移除了。简而言之,看起来删除它会导致生成完全相同的代码,所以尽管去做吧。

我不确定是否有任何优化,但我的看法是:

这是由于一些代码修改造成的,删除了一个主要条件(比方说,初始 if块中的函数调用) ,但是开发人员/维护人员

因此,他们没有删除相关的 if块,而是简单地将条件更改为 if(0)并继续前进。

正如前面所说的,零被计算为 false,并且分支可能会被编译器优化掉。

我以前也在代码中看到过这种情况,在代码中添加了一个新的特性,需要一个终止开关(如果这个特性出了问题,你可以直接关闭它) ,一段时间后,当终止开关被删除时,程序员也没有删除这个分支,例如。

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

变成了

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

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

where 1 = 1

这可能使添加其他条件变得更加容易,因为所有其他条件都可以用 and预先处理,而不必额外检查它是否是第一个条件。

我见过几次这样的情况,我认为最可能的原因是它正在计算代码的旧版本/不同版本/分支中的某些内容,或者可能是为了调试,而将其改为 if(0)是一种有点懒惰的方式来删除那里的内容。

按照编写的内容,if (0) {}子句编译为空。

我怀疑这个梯子顶部的子句的功能是通过将 0改为 1或者 true,提供一个容易的地方来暂时禁用所有其他功能(为了调试或者比较的目的)。

如果有 #if语句,那么这将非常有用,ala

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

等等。

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

我看到过使用模板语言生成的预扩展 JavaScript 中不可到达的代码块。

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

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

它曾经被预先编译过:

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

希望这有助于你相对的潜在低键盘活动的亲回收编码器时代,我表现出热情!

这是代码腐烂

在某个时刻,“ if”做了一些有用的事情,情况发生了变化,可能被评估的变量被删除了。

修改系统的人做了 尽可能少地影响系统的逻辑,所以他只是确保代码能够重新编译。所以他留下了一个“如果(0)”,因为这很快,很容易,他不完全确定这是他想要做的。他让系统运转起来,却没有回去彻底修复它。

然后下一个开发人员出现了,认为这是故意的 ,只注释掉了代码的这部分(因为它本来就没有被评估) ,然后下次再碰到代码的时候,这些注释就被删除了。

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

调试通常是在未经优化的代码上进行的,因此总是错误的测试将会出现,并且能够在其上设置断点。在为生产环境编译代码时,这行代码将被优化。这条看似无用的线为开发和测试构建提供了功能,而不会影响发布构建。

上面还有其他好的建议; 真正知道目的是什么的唯一方法,就是找到作者并询问。您的源代码控制系统可能对此有所帮助。(查找 blame类型的功能。)

这个结构也可以在 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

它有助于调试这个块,只是把 if 块1。这将禁用所有 if else 块功能。我们也可以展开 if else 块。

@ 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命令语言]。

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

我在编写生成其他程序的程序时就开始做这类事情,在那些程序特别方便的地方。

当我们使用 Intel 的旧 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 逻辑,但适用于短路 & & 和 | | 。

    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.

例如,我见过这种方法用于处理错误

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


if(condition_fails)
goto lable1;

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