为什么使用这个结构?疯狂还是天才?

时间:2013-10-17 10:40:49

标签: c coding-style

我正在使用从Linus Torvalds到不明身份的Elbonian代码奴隶的各种不同质量/能力/理智来源的大型SDK代码库。

有各种各样的代码风格,有些风格明显优于其他代码,并且它证明了一个有趣的机会,可以通过其他措施扩展我对人类未来的知识/绝望。

我刚刚遇到一堆功能,这些功能反复使用一种略显奇怪的(对我来说)风格,即:

void do_thing(foo)
{
    do {
        if(this_works(foo) != success)
            break;
        return(yeah_cool);
    } while (0);
    return(failure_shame_death);
}

在这段代码中没有做任何复杂的事情(我没有为这篇文章减少10,000行的魔法),他们可以轻松地做到:

if(this_works(foo) == success)
    return(yeah_cool);
else
    return(failure_shame_death);

这似乎更好/更整洁/更直观/更容易阅读。

所以我现在想知道是否有一些(好)理由以另一种方式做到这一点,还是只是他们总是在Elbonian Code Mines中这样做?

编辑:根据“可能的重复”链接,此代码不会在任何类型的宏中进行预处理,它只是在普通代码中。我可以相信这可能是由于关于错误检查的编码样式规则as per this answer

5 个答案:

答案 0 :(得分:8)

另一个猜测:也许你没有正确引用原始代码?我已经看到了想要避免goto的人使用的相同模式:他们使用do-while(0)循环,最后返回成功值。他们还可以break退出循环以进行错误处理:

int doXandY() {
   do {
      if (!x()) {
         break;
     }

     if (!y()) {
         break;
     }
     return 0;
   } while( 0 );

   /* Error handling code goes here. */
   globalErrorFlag = 12345;
   return -1;
}

在你的例子中没有太多指向它,因为循环非常短(即只有一个错误情况)并且错误处理代码只是return,但我怀疑在实际代码中它可以是更复杂。

答案 1 :(得分:5)

嗯,代码可能会以某种方式进行预处理。 do { } while(0)是预处理器宏中使用的技巧;你可以像这样定义它们:

#define some_macro(a) do { whatever(); } while(0)

优点是你可以在任何地方使用它们,因为允许在while(0)之后加一个分号,就像你上面的代码一样。

原因是如果你写

#define some_macro(a) { whatever(); }

if (some_condition)
    some_macro(123);
else
    printf("this can cause problems\n");

由于在else语句之前有一个额外的分号,因此该代码无效。 do { ... } while(0)可以在任何地方使用。

答案 2 :(得分:5)

有些人在循环中使用do{} while(0);构造与break;在某种程度上符合MISRA规则14.7。此规则表示函数中只能有单个进入和退出点。安全标准ISO26262也要求此规则。请找一个示例函数:

int32_t MODULE_some_function(bool first_condition,bool second_condition)
{
    int32_t ret = -1 ;

    do
    {
        if(first_condition)
        {
            ret = 0 ;
            break ;
        }

        /* some code here */ 

        if(second_condition)
        {
            ret = 0 ;
            break ;
        }

        /* some code here */ 

    } while(0) ;

    return ret ;
}

但请注意,上面显示的这种结构违反了规则14.6中的不同MISRA规则。编写这样的代码,你将符合一个MISRA规则,据我所知,人们使用这样的结构作为使用函数的多个返回的变通方法。

在我看来,do{}while(0);构造的实际用法确实存在于构造某些类型的宏的方式中。请检查下面的问题,这对我很有帮助:

Why use apparently meaningless do-while and if-else statements in macros?

值得注意的是,在某些情况下,如果使用适当的优化选项编译代码,do{}while(0);构造将完全被优化掉。

答案 3 :(得分:0)

我猜这个代码最初是用goto编写的,用于错误处理:

void do_thing(foo)
{
    if(this_works(foo) != success)
        goto error;
    return(yeah_cool);
error:
    return(failure_shame_death);
}

但是在某些时候,一个法令从高处下来“你不能使用goto”,所以有人做了从goto样式到循环中断样式的半自动翻译(也许用简单的脚本)。可能是代码被合并/从一个项目移动到另一个项目时。

答案 4 :(得分:0)

做{...}而(0)以“break”排列是某种“普通C的RAII”。

这里,“break”被视为异常范围退出(类型为“Plain C exceptions”),因此您可以确定只有一个地方可以释放资源:在“while(0)”之后。这似乎有点不寻常,但实际上它在普通C世界中非常普遍。