C: do {...} while(0)?

13

1
重复:https://dev59.com/qXVC5IYBdhLWcg3w4Vb6 - sdcvvc
这个宏的一个问题是:类似 SAFE_FREE(get_buffer()) 这样的语句无法编译通过。在创建类似这样的宏时需要考虑这一点。 - Dan Moulding
1
如果 ((x) != NULL) 是多余的。你可以安全地删除它。free(NULL) 什么也不做,为什么要浪费一个 if 呢? - N 1.1
@Dan Moulding:这就是这个结构的意义所在——它需要一个分号,因此看起来就像函数调用一样。 - Cascabel
1
@N1.1 free(NULL); 是未定义行为,仅仅因为所有现代操作系统都会检查它并不意味着它是安全的。 - yyny
显示剩余4条评论
4个回答

24
do { stuff() } while(0);

做的事情与stuff()完全相同。那么问题在哪里呢?问题在于宏的语法。假设我们定义了以下宏:

#define SAFE_FREE(x) if ((x) != NULL) { free(x); x=NULL; }

接下来,有两个问题。第一个问题相对较小:使用SAFE_FREE不再需要尾随的分号。然而更重要的是,像下面这样的代码:

if (...)
  SAFE_FREE(x)
else
   stuff();

将扩展为:

if (...)
  if ((x) != NULL) {
    free(x);
    x = NULL;
  } else
    stuff();

请注意,现在else与错误的if语句匹配。这完全改变了程序流程,尽管在源代码中看起来并不像那样。
如上所述定义宏可以防止奇怪的行为,因为do { ... } while(0)就像没有分号的语句一样。

1
关于“我不知道像MSVC、gcc、icc、lcc等编译器是否会像他们应该做的那样删除循环,但是当涉及到编译代码时,优化肯定是一个难以解决的问题。”你甚至不需要一个特别好的编译器。这是一个如此常见的结构,即使关闭了优化,GCC也会将其优化掉。这甚至不是一种优化——让代码运行更快的东西——本质上只是一种消除不必要语法的微不足道的方法。 - jade
即使关闭了优化,gcc也不会将其优化掉。如果在关闭优化时gcc对代码进行重构,那么调试将会成为一场噩梦。 - Jason Coco
2
@Jason Coo:嗯...是的。我验证了一个简单的例子,包括和不包括“do {...} while(0);”包装器,使用-O0和默认选项。gcc给了我完全相同的输出。(4.3.2,Debian 4.3.2-1.1,在Debian lenny上。) - jade
1
为什么不只使用一个代码块呢?似乎do while(0)仍然是多余的,可以通过一组花括号来实现,就像上面的代码所示。我能看到while循环中唯一的原因是分号。 - Billy ONeal
@Jason 调试会怎样变成噩梦? - Johannes Schaub - litb

15

do while是一种常见的约定,与标准c函数一样要求宏需要有分号作为结尾。除此之外,它只是确保已释放的变量被设置为NULL,这样任何未来对其进行释放的调用都不会导致错误。


1
do/while(0) 的主要目的是避免扩展代码与程序员意图非常不同。 - janm
1
@janm 可以通过使用大括号来实现,而不需要看起来毫无用处的 do/while(0)。例如:#define SAFE_FREE(x) { if ((x) != NULL) {free(x); x=NULL;} } - user120587
1
@Brandon Bodnár:不行。考虑使用“if(test)SAFE_FREE(x); else blah();”会出现语法错误。 - janm
@BrandonBodnar 在大多数编译器中,将代码块用括号括起来 ({ /* ... */ }) 会被解析为单个表达式,但这在 ANSI C 中是非标准的(我认为 C90 已经修复了这个问题)。 - yyny
编辑:C90无法解决这个问题:c - yyny
显示剩余3条评论

2
顺便提一下,在C++ 风格和技巧 FAQ中,Bjarne Stroustrup建议使用内联(模板)函数来执行“删除并置空”的操作。
template<class T> inline void destroy(T*& p) { delete p; p = 0; } 

1
如果 C 有模板就太好了 :) - jmh
@jmh 哈哈,为什么?void* 大师种族。 - yyny

1

do/while(0) 的想法是,您可以在不出现意外错误的情况下使用宏,就像使用函数调用一样。

例如,如果您有以下代码:

if (today_is_tuesday())
    SAFE_FREE(x);
else
    eat_lunch();

而宏只是:

#define SAFE_FREE(x)  if (x) { free(x); x = 0; }

你会得到非常不同的结果。do/while约定通过使其表现一致来避免这些错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接