【问题标题】:The need for parentheses in macros in C [duplicate]C中宏中需要括号[重复]
【发布时间】:2012-06-04 22:15:42
【问题描述】:

我尝试在以下代码中使用宏 SQR 的定义:

#define SQR(x) (x*x)
int main()
{
    int a, b=3;
    a = SQR(b+5);      // Ideally should be replaced with (3+5*5+3), though not sure.
    printf("%d\n",a);
    return 0;
}

它打印23。如果我将宏定义更改为SQR(x) ((x)*(x)),则输出与预期一致,64。我知道在 C 中调用宏会用宏的定义替换调用,但我仍然无法理解,它是如何计算的 23

【问题讨论】:

标签: c macros c-preprocessor parentheses


【解决方案1】:

考虑使用这个宏替换宏:

#define SQR(x) (x*x)

使用b+5 作为参数。自己更换。在您的代码中,SQR(b+5) 将变为:(b+5*b+5)(3+5*3+5)。现在记住您的运算符优先级规则:*+ 之前。所以这被评估为:(3+15+5),或23

宏的第二个版本:

#define SQR(x) ((x) * (x))

是正确的,因为您使用括号来保护宏参数免受运算符优先级的影响。

This page 解释操作员对 C 的偏好有一个很好的图表。 Here'sC11参考文档的相关部分。

这里要记住的是,您应该养成始终使用括号屏蔽宏中的任何参数的习惯。

【讨论】:

    【解决方案2】:

    只需将宏扩展中的每个参数括在括号中即可。

    #define SQR(x) ((x)*(x))
    

    这适用于您传递的任何参数或值。

    【讨论】:

    • 没有解释,没有回答问题(如何宏扩展工作),格式错误,没有附加值。因此没有理由存在这个答案。
    【解决方案3】:

    因为宏只是字符串替换,它发生在完成过程之前。编译器将没有机会看到宏变量及其值。例如:如果一个宏被定义为

    #define BAD_SQUARE(x)  x * x 
    

    这样称呼

    BAD_SQUARE(2+1) 
    

    编译器会看到这个

    2 + 1 * 2 + 1
    

    这可能会导致

    的意外结果
    5
    

    要纠正这种行为,您应该始终用括号括住宏变量,例如

    #define GOOD_SQUARE(x)  (x) * (x) 
    

    当这个宏被调用时,例如,像这样

    GOOD_SQUARE(2+1)
    

    编译器会看到这个

    (2 + 1) * (2 + 1)
    

    这将导致

    9
    

    另外,这里有一个完整的例子来进一步说明这一点

    #include <stdio.h>
    
    #define BAD_SQUARE(x)  x * x 
    // In macros alsways srround the variables with parenthesis
    #define GOOD_SQUARE(x)  (x) * (x) 
    
    int main(int argc, char const *argv[])
    {
        printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) ); 
        printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) ); 
        printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \
    subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) ); 
        printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \
    subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) ); 
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      经过预处理,SQR(b+5) 将扩展为(b+5*b+5)。这显然是不正确的。

      SQR的定义有两个常见错误:

      1. 不要将宏的参数括在宏体中的括号中,因此如果这些参数是表达式,则这些表达式中具有不同优先级的运算符可能会导致问题。这是解决此问题的版本

        #define SQR(x) ((x)*(x))
        
      2. 多次评估宏的参数,因此如果这些参数是具有副作用的表达式,则这些副作用可能会被多次使用。例如,考虑SQR(++x) 的结果。

        通过使用 GCC typeof 扩展,这个问题可以这样解决

        #define SQR(x) ({ typeof (x) _x = (x); _x * _x; })
        

      这两个问题都可以通过用内联函数替换宏来解决

         inline int SQR(x) { return x * x; }
      

      这需要 GCC 内联扩展或 C99,请参阅 6.40 An Inline Function is As Fast As a Macro

      【讨论】:

        【解决方案5】:

        预处理器宏在编译代码之前执行文本替换,因此 SQR(b+5) 转换为 (b+5*b+5) = (6b+5) = 6*3+5 = 23

        常规函数调用会在将参数 (b+3) 传递给函数之前计算其值,但由于宏是预编译替换,因此运算的代数顺序变得非常重要。

        【讨论】:

          【解决方案6】:

          宏只是一个直接的文本替换。预处理后,您的代码如下所示:

          int main()
          {
              int a, b=3;
              a = b+5*b+5;
              printf("%d\n",a);
              return 0;
          }
          

          乘法的运算符优先级高于加法,因此在计算a 的值时,它在两次加法之前完成。在宏定义中添加括号可以解决问题:

          int main()
          {
              int a, b=3;
              a = (b+5)*(b+5);
              printf("%d\n",a);
              return 0;
          }
          

          括号内的运算在乘法之前进行计算,因此现在首先进行加法,您会得到您所期望的a = 64 结果。

          【讨论】:

            【解决方案7】:

            因为(3+5*3+5 == 23)

            ((3+5)*(3+5)) == 64.

            最好的方法是不使用宏

            inline int SQR(int x) { return x*x; }
            

            或者干脆写x*x

            【讨论】:

              【解决方案8】:

              宏扩展为

               a = b+5*b+5;
              

               a = b + (5*b) + 5;
              

              所以 23。

              【讨论】:

                猜你喜欢
                • 2021-07-22
                • 1970-01-01
                • 2011-08-14
                • 1970-01-01
                • 2023-01-03
                • 1970-01-01
                • 2012-11-25
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多