do {} while(0) - 宏观安全

时间:2013-10-01 12:28:11

标签: c macros scope do-while

我们知道多行宏必须在do while(0)循环中包含,以便它们安全地包含在代码中的任何位置。

例如,这是100%安全的:

#define swap( a , b )       \
        do                  \
        {                   \
            int t = a ;     \
            a = b ;         \
            b = t ;         \
        }                   \
        while(0)

我想知道在循环外部有一部分代码的宏是否也是安全的,如果不是,在什么情况下会出现问题。

#define swap( a , b )       \
        int t ;             \   //should be in the same scope where the macro was called and before the loop
        do                  \
        {                   \
            t = a ;         \
            a = b ;         \
            b = t ;         \
        }                   \
        while(0)

是否有一种安全的方法可以实现这一点,如果不安全,我必须更改哪些规则或遵循哪些规则。

5 个答案:

答案 0 :(得分:3)

在许多方面都不安全。首先,如果已经存在任何名为t的变量,则无法编译。

除此之外,你必须小心:

if (...)
    swap(a,b);

因为它将扩展为:

if (...)
    int t;
    do { ... } while(0);

请注意,if主体只是一个声明,因为if块没有大括号。

为什么不在do块中声明?

答案 1 :(得分:2)

我能想到的两个案例。首先,如果当前范围中已存在变量t,则会在同一范围错误中获得多个定义或冲突定义。其次,考虑一下:

if (some_condition) swap(a,b);

将扩展为:

if (some_condition) int t;
do
{
    t = a;
    a = b;
    b = t;
} while (0);

答案 2 :(得分:1)

如果名为t的变量在该范围内的任何其他位置,则编译安全。

答案 3 :(得分:1)

除了范围故事之外,你的宏还有第二个问题。你忘了在你的宏变量中加上括号,这可能会产生一些惊人的效果。

#define swap( a , b )       \
        do                  \
        {                   \
            int t = a ;     \
            a = b ;         \
            b = t ;         \
        }                   \
        while(0)

在C ++中,如果您使用三元组swap(x?x:y, z)调用宏,则会产生令人惊讶的效果。

do
  {
  int t = x?x:y;
  x?x:y = z ;
  z = t;
  }
while(0)

由于三元的优先级低于做作,第二行将被解释为: x?x:(y = z);

可能还有其他的惊喜,因此通常:

始终将括号括在宏参数周​​围,总是!

编辑:这是正确的#define

#define swap( a , b )       \
        do                  \
        {                   \
            int t = (a) ;     \
            (a) = (b) ;         \
            (b) = t ;         \
        }                   \
        while(0)

答案 4 :(得分:0)

由于您的实际目标是定义t以便从makro外部进行访问,因此您应该在makro之外声明它。如果您的makro可以在一个代码块中多次使用(例如您用作示例的swap - 您希望能够交换多个值),那么获得t的多个声明的问题。

相反,您可以像使用USES_CONVERSION的MFC一样执行此操作,并定义另一个为您的makro准备所有“全局”数据的makro USES_SWAP,并且可以在每个代码块中调用一次,之后以及SWAP的所有外观(理想情况下,在代码块的开头)。现在你可以随意交换东西,并且仍然可以在一个makro中处理所需变量的声明。

当然,问题仍然是t中声明的USES_SWAP会与其他t发生冲突,但如果您找不到另一种方式,那么这可能是必要的做到这一点。你应该尝试做什么;)为了减少这个问题的可能性,你应该考虑一个更具体的名称,例如swap_t甚至SwapInternalVariable_t。在普通代码中有人用该名称声明变量的危险性非常低。