【问题标题】:do{}while(0) - macro safetydo{}while(0) - 宏安全
【发布时间】:2013-10-07 14:41:20
【问题描述】:

我们知道多行宏必须包含在 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)

有没有一种安全的方法来实现这一点,必须改变什么或者如果不安全我应该遵循什么规则。

【问题讨论】:

  • 你为什么要这样做?

标签: c macros scope do-while


【解决方案1】:

它在很多方面都不安全。首先,如果任何名为 t 的变量已经存在,它就不会编译。

除此之外,您还必须注意以下事项:

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

因为它将被扩展为:

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

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

为什么不在 do 块中声明 t?

【讨论】:

  • 我在街区外需要它。
  • 然后将其包裹在另一个 do while 块中,如下所示: do { int t; do { /* swap... */ } while (0); } 而 (0);
  • 我需要它在宏块之外:)
  • 啊,好吧...嗯,我想你必须忍受这个并坚持你的原始代码。
  • 是的,会的。通常,您必须小心任何可以放置大括号的说明(if、while、for 等)。但如果你总是包含它们,应该没有问题。
【解决方案2】:

我能想到的两种情况。首先,如果当前范围内已经存在变量t,您将在同一范围内得到多个定义或冲突定义错误。二、考虑:

if (some_condition) swap(a,b);

将扩展为:

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

【讨论】:

  • if 语句是唯一可能发生这种情况的地方吗?如果我在任何地方都使用 if{}else{} 那么我不明白吗?
  • 不,如果将if 替换为forwhile,您也会遇到同样的问题。您可能会遇到 switch 语句的问题。如果您尝试将它与任何短路逻辑运算符 ||&& 一起使用,您将遇到问题。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    除了范围故事之外,您的宏还有第二个问题。您忘记给宏变量加上括号,这可能会产生一些意想不到的效果。

    #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)
    

    【讨论】:

    • 我的编译器不允许我在swap 宏中添加三元运算符(需要左值)。我必须说我已经阅读了所有关于在宏变量周围加上括号的内容,但从来没有在宏参数周​​围。你能提供一些信息(链接)吗?谢谢。
    • 这里可能有一个误解,在#define 中,每个宏参数的出现都应该放在括号中,而不是在调用点。
    • 我没有找到一个例子来说明 C 中的问题,这就是为什么我在 C++ 中说的原因,因为三元的优先级与 C 中的不同,并且它们被允许为 lvalue。这并不意味着不会有其他问题。
    【解决方案5】:

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

    相反,您可以像 MFC 一样使用 USES_CONVERSION 并定义另一个 makro USES_SWAP 为您的 makro 准备所有“全局”数据,并且在每个代码块中只能调用一次,在任何和所有出现之前SWAP(最好是在代码块的开头)。现在,您可以根据需要随时交换内容,并且仍然可以在一个 makro 中处理所需变量的声明。

    当然,问题仍然是在USES_SWAP 中声明的t 将与其他ts 冲突,但如果您找不到其他方法,这可能只是必要的。您应该尝试这样做 ;) 为了减少这成为问题的可能性,您应该考虑使用更具体的名称,例如 swap_t 甚至 SwapInternalVariable_t。在普通代码中使用该名称声明变量的危险非常低。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 2012-05-30
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 2015-04-30
      • 2010-10-29
      相关资源
      最近更新 更多