17

我正在使用一个庞大的 SDK 代码库,这些代码库从不同质量/能力/理智的各种来源汇聚在一起,从 Linus Torvalds 到身份不明的 Elbonian 代码奴隶。

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

我刚刚遇到一堆函数,它们反复使用(对我来说)有点奇怪的风格,即:

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 中总是这样做的方式?

编辑:根据“可能重复”链接,此代码在任何类型的宏中进行预处理,它只是在普通代码中。我可以相信这可能是由于关于错误检查的编码风格规则,根据这个答案

4

5 回答 5

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;
}

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

于 2013-10-17T12:30:38.147 回答
6

有些人在循环内部使用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 ;
}

但是请注意,我上面显示的这种结构违反了不同的 MISRA 规则,即规则 14.6。编写这样的代码您将符合一个 MISRA 规则,据我所知,人们使用这样的构造作为解决方法,以防止使用函数的多个返回。

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

为什么在宏中使用看似毫无意义的 do-while 和 if-else 语句?

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

于 2013-10-17T14:05:19.280 回答
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)在任何地方工作。

于 2013-10-17T10:49:32.993 回答
1

do {...} while(0) 与“break”一起排列是某种“RAII for Plain C”。

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

于 2013-10-21T16:31:48.363 回答
0

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

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

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

于 2013-10-21T16:23:36.533 回答