C语言的宏之带参数宏

秒速五厘米 2023-02-20 12:20 92阅读 0赞

C语言的宏之带参数宏

在这里插入图片描述

引言

C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数,这种宏外形和作用与函数类似,即类函数宏。但是它的行为和函数调用完全不同。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。类函数宏定义的圆括号中可以有一个或多个参数,随后这些参数出现在替换体中。

示例分析

下面是一个类函数宏的示例:

  1. #define SQUARE(X) X*X

在程序中可以这样使用:

  1. z= SQUARE(2);

这看上去像函数调用,但是它的行为和函数调用完全不同。

再看下面的示例:

  1. /* mac_arg.c -- macros with arguments */
  2. #include <stdio.h>
  3. #define SQUARE(X) X*X
  4. #define PR(X) printf("The result is %d.\n", X)
  5. int main(void)
  6. {
  7. int x = 5;
  8. int z;
  9. printf("x = %d\n", x);
  10. z = SQUARE(x);
  11. printf("Evaluating SQUARE(x): ");
  12. PR(z);
  13. z = SQUARE(2);
  14. printf("Evaluating SQUARE(2): ");
  15. PR(z);
  16. printf("Evaluating SQUARE(x+2): ");
  17. PR(SQUARE(x+2));
  18. printf("Evaluating 100/SQUARE(2): ");
  19. PR(100/SQUARE(2));
  20. printf("x is %d.\n", x);
  21. printf("Evaluating SQUARE(++x): ");
  22. PR(SQUARE(++x));
  23. printf("After incrementing, x is %x.\n", x);
  24. return 0;
  25. }

输出结果如下,你的编译器输出甚至有可能与下面的结果完全不同。

  1. x = 5
  2. Evaluating SQUARE(x): The result is 25.
  3. Evaluating SQUARE(2): The result is 4.
  4. Evaluating SQUARE(x+2): The result is 17.
  5. Evaluating 100/SQUARE(2): The result is 100.
  6. x is 5.
  7. Evaluating SQUARE(++x): The result is 49.
  8. After incrementing, x is 7.

程序中, SQUARE(2)替换为2*2,x实际上起到参数的作用。前两行与预期相符,但是接下来的结果有点奇怪。导致这样结果的原因是预处理器 不做计算、不求值,只替换字符序列。对于SQUARE(x+2),预处理器替换为 x+2*x+2。如果x=5,那么表达式的值为:

  1. 5+2*5+2 = 5+10+2=17

宏定义的时候多加几个括号可以解决这样的问题:

  1. #define SQUARE(X) (X)*(X)

现在SQUARE(2)变成(x+2)*(x+2),可以得到正确结果。

但是加括号不能解决所有问题。最后一种情况的问题还是无法避免。

SQUARE(++x)变成了++x*++x,递增了两次,一次在乘法运算前,一次在乘法运算后:

  1. ++x*++x=6*7=42

由于标准并未对这类运算规定顺序,所以有些编译器得6*7。而有些编译器可能在乘法运算之前已经递增了x,所以7*7得49。在C标准中,对该表达式求值的这种情况称为未定义行为。无论哪种情况,x的开始值是5,虽然从源代码上看只递增了一次,但是x的最终值是7。

解决这个问题最简单的方法是,避免用++x作为宏参数。一般而言,不要在宏中使用递增或递减运算符。但是,++x可作为函数参数,因为编译器会对++x求值后,再把5传给函数。


参考资料:

史蒂芬・普拉达. C Primer Plus (第6版) 中文版[M]. 人民邮电出版社, 2016.

发表评论

表情:
评论列表 (有 0 条评论,92人围观)

还没有评论,来说两句吧...

相关阅读