我正在使用从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。
答案 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世界中非常普遍。