空枝|C|深入理解库中随处可见的宏( 二 )


/* macros with arguments */#include #define SQUARE(X) X*X#define PR(X)printf("The result is %d.\n", X)int main(void){int x = 5;int z;printf("x = %d\n", x);z = SQUARE(x);printf("Evaluating SQUARE(x): ");PR(z);z = SQUARE(2);printf("Evaluating SQUARE(2): ");PR(z);printf("Evaluating SQUARE(x+2): ");PR(SQUARE(x+2));printf("Evaluating 100/SQUARE(2): ");PR(100/SQUARE(2));printf("x is %d.\n", x);printf("Evaluating SQUARE(++x): ");PR(SQUARE(++x));printf("After incrementing, x is %x.\n", x);getchar();return 0;}/*x = 5Evaluating SQUARE(x): The result is 25.Evaluating SQUARE(2): The result is 4.Evaluating SQUARE(x+2): The result is 17.Evaluating 100/SQUARE(2): The result is 100.x is 5.Evaluating SQUARE(++x): The result is 49.After incrementing, x is 7.*/SQUARE(x+2)展开后:
SQUARE(x+2*x+2) ,
因为运算符的优先级不一样 , 所以计算的结果不同于预期 。 如果是函数参数 , 会先计算x+2 , 就不存在优先级的区别 。 是否有改善的办法呢?我们知道 , 圆括号可以有最高的优先级 , 所以需要用足够多的圆括号来确保最高优先级(就像预先做了求值一样) , 单个参数使用圆括号 , 整体也使用圆括号:
#define SQUARE(X) ((X)*(X))
但对于SQUARE(++x) , 展开后是:
++x*++x
x经过了两次自增 , 并不是期望的一次自增 。 所以 , 一般尽量不要做宏中使用自增运算符 。
3 用宏参数创建字符串:#运算符在第一个实例中说到双引号中的字符 , 即使有定义宏 , 也不做替换 , 宏参数例外 , C允许在字符串中包含宏参数 , 但宏参数需以字符#开头 , 就像特殊的转义字符一样 。
看下面的实例:
/* substitute in string */#include #define PSQR(x) printf("The square of " #x " is %d.\n",((x)*(x)))int main(void){int y = 5;PSQR(y);PSQR(2 + 4);getchar();return 0;}/*The square of y is 25.The square of 2 + 4 is 36.*/3 用宏参数连结字符串:##运算符与#运算符类似 , ##运算符可用于类函数宏的替换部分 , 能够连结字符串 , 组成一个新的标识符:
// use the ## operator#include #define XNAME(n) x ## n#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n);int main(void){int XNAME(1) = 14;// becomes int x1 = 14;int XNAME(2) = 20;// becomes int x2 = 20;int x3 = 30;PRINT_XN(1);// becomes printf("x1 = %d\n", x1);PRINT_XN(2);// becomes printf("x2 = %d\n", x2);PRINT_XN(3);// becomes printf("x3 = %d\n", x3); getchar();return 0;}/*x1 = 14x2 = 20x3 = 30*/4 变参宏:...和__VA_ARGS__stdarg.h提供的可变参数使用的就是宏定义:
typedef char *va_list;#define _INTSIZEOF(n)( (sizeof(n) + sizeof(int) - 1)double y;y = sqrt(x);PR(1, "x = %g\n", x);PR(2, "x = %.2f, y = %.4f\n", x, y);getchar();return 0;}/*Message 1: x = 48Message 2: x = 48.00, y = 6.9282 */5 宏和函数的选择有些编程任务既可以用带参数的宏完成 , 也可以用函数完成 。 各自有适应的一些场合 。
使用宏比使用普通函数复杂一些 , 稍有不慎会产生奇怪的副作用 。
宏和函数的选择实际上是时间和空间的权衡 。 宏展开产生内联代码 , 略占空间 , 但没有普通函数的跳转 , 时间较省 。 特别是用在嵌套循环体内时 。 当然 , 在C++中 , 有一种更优的替换方案 , 就是使用inline内联函数 。
C作为一种强类型语言 , 普通函数需要明确数据类型 , 而宏不存在这一限制 , 不用担心变量类型 , 因为其本身就只是做字符替换 , 并不做变量类型检查 , 当然这也是宏的另一种缺陷 。


推荐阅读