c语⾔中带参数define,C语⾔之在#define中使⽤参数-诺⽲在#define中使⽤参数可以创建外形和作⽤与函数类似的类函数宏。带有参数的宏看上去很像函数,因为这样的宏也使⽤圆括号。类函数宏定义的圆括号中可以有⼀个或多个参数,随后这些参数出现在替换体中,如下图所⽰。
C语⾔之在#define中使⽤参数
Parts of a function-like macro definition
下⾯是⼀个类函数宏的⽰例:
#define SQUARE(X) X*X
在程序中可以这样⽤:
z = SQUARE(2);
这看上去像函数调⽤,但是它的⾏为和函数调⽤完全不同。程序macarg.c 演⽰了类函数宏和另⼀个宏的⽤法。该⽰例中有⼀些陷阱,请读者仔细阅读序。
The macarg.c Program
/
mac_arg.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 = %dn", 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);
return 0;
}
SQUARE宏的定义如下:
#define SQUARE(X) XX
这⾥,SQUARE是宏标识符,SQUARE(X)中的X是宏参数,XX是替换列表。程序清单16.2中出现SQUARE(X)的地⽅都会被XX替换。这与前⾯的⽰例不同,使⽤该宏时,既可以⽤X,也可以⽤其他符号。宏定义中的X由宏调⽤中的符号代替。因此,SQUARE(2)替换为
22,X实际上起到参数的作⽤。 然⽽,稍后你将看到,宏参数与函数参数不完全相同。下⾯是程序的输出。注意有些内容可能与我们的预期不符。实际上,你的编译器输出甚⾄与下⾯的结果完全不同。
x = 5 Evaluating 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 42. After incrementing, x
is 7.
前两⾏与预期相符,但是接下来的结果有点奇怪。程序中设置x的值为5,你可能认为SQUARE(x+2)应该是77,即49。但是,输出的结果是17,这不是⼀个平⽅值!导致这样结果的原因是,我们前⾯提到过,预处理器不做计算、不求值,只替换字符序列。预处理器把出现x的地⽅都替换成x+2。因此,xx变成了x+2x+2。如果x为5,那么该表达式的值为: xx 变成了 x+2*x+2。
如果x为5,那么该表达式的值为:
5+2*5+2 = 5 + 10 + 2 = 17
该例演⽰了函数调⽤和宏调⽤的重要区别。函数调⽤在程序运⾏时把参数的值传递给函数。宏调⽤在编译之前把参数记号传递给程序。这两个不同的过程发⽣在不同时期。是否可以修改宏定义让SQUARE(x+2)得36?当然可以,要多加⼏个圆括号:
#define SQUARE(x) (x)(x)
现在SQUARE(x+2)变成了(x+2)(x+2),在替换字符串中使⽤圆括号就得到符合预期的乘法运算。但是,这并未解决所有的问题。下⾯的输出⾏:
100/SQUARE(2)
将变成:
100/22
根据优先级规则,从左往右对表达式求值:(100/2)2,即50*2,得100。把SQUARE(x)定义为下⾯的形式可以解决这种混乱:
#define SQUARE(x) (xx)
这样修改定义后得100/(22),即100/4,得25。要处理前⾯的两种情况,要这样定义:
#define SQUARE(x) ((x)(x))
因此,必要时要使⽤⾜够多的圆括号来确保运算和结合的正确顺序。尽管如此,这样做还是⽆法避免程序中最后⼀种情况的问题。SQUARE(++x)变成了++x++x,递增了两次x,⼀次在乘法运算之前,⼀次在乘法运算之后:
SQUARE(++x)
变成了:
++x*++x
递增了两次x,⼀次在乘法运算之前,⼀次在乘法运算之后:
++x++x = 67 = 42
由于标准并未对这类运算规定顺序,所以有些编译器得76。⽽有些编译器可能在乘法运算之前已经递增了x,所以77得49。在C标准中,对该表达式求值的这种情况称为未定义⾏为。⽆论哪种情况,x的开始值都是5,虽然从代码上看只递增了⼀次,但是x的最终值是7。解决这个问题最简单的⽅法是,避免⽤++x作为宏参数。⼀般⽽⾔,不要在宏中使⽤递增或递减运算符。但是,++x可作为函数参数,因为编译器会对++x求值得5后,再把5传递给函数。
1 ⽤宏参数创建字符串:#运算符
下⾯是⼀个类函数宏:
#define PSQR(X) printf("The square of X is %d.n", ((X)*(X)));
假设这样使⽤宏:
PSQR(8);
输出为:
The square of X is 64.
注意双引号字符串中的X被视为普通⽂本,⽽不是⼀个可被替换的记号。C允许在字符串中包含宏参数。在类函数宏的替换体中,#号作为⼀个预处理运算符,可以把记号转换成字符串。例如,如果x是⼀个宏形参,那么#x就是转换为字符串"x"的形参名。这个过程称为字符串化(stringizing)。程序清单16.3演⽰了该过程的⽤法。
Listing 16.3 The subst.c Program
/subst.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);
return 0;
}
该程序的输出如下:
The square of y is 25. The square of 2 + 4 is 36.
调⽤第1个宏时,⽤"y"替换#x。调⽤第2个宏时,⽤"2+4"替换#x。ANSI C字符串的串联特性将这些字符串与printf()语句的其他字符串组合,⽣成最终的字符串。例如,第1次调⽤变成:
printf("The square of " "y" " is %d.n",((y)*(y)));
然后,字符串串联功能将这3个相邻的字符串组合成⼀个字符串:
"The square of y is %d.\n"
2 预处理器黏合剂:##运算符
与#运算符类似,##运算符可⽤于类函数宏的替换部分。⽽且,##还可⽤于对象宏的替换部分。##运算符把两个记号组合成⼀个记号。例如,可以这样做:
#define XNAME(n) x ## n
然后,宏XNAME(4)将展开为x4。程序清单16.4演⽰了##作为记号粘合剂的⽤法。
Listing 16.4 The glue.c Program
// glue.c -- use the ## operator
#include
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n " = %dn", 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 = %dn", x1);
PRINT_XN(2); // becomes printf("x2 = %dn", x2);
PRINT_XN(3); // becomes printf("x3 = %dn", x3);
return 0;
}
该程序的输出如下:
x1 = 14
x2 = 20
x3 = 30
注意,PRINTXN()宏⽤#运算符组合字符串,##运算符把记号组合为⼀个新的标识符。
3 变参宏:…和VAARGS
⼀些函数(如printf())接受数量可变的参数。stdvar.h头⽂件(本章后⾯介绍)提供了⼯具,让⽤户⾃定义带可变参数的函数。C99/C11也对宏提供了这样的⼯具。虽然标准中未使⽤“可变”(variadic)这个词,但是它已成为描述这种⼯具的通⽤词(虽然,C标准的索引添加了字符串化(stringizing)词条,但是,标准并未把固定参数的函数或宏称为固定函数和不变宏)。通过把宏参数列表中最后的参数写成省略号(即,3个点…)来实现这⼀功能。这样,预定义宏 _ VAARGS 可⽤在替换部分中,表明省略号代表什么。例如,下⾯的定义:
#define PR(...) printf(__VA_ARGS__)
假设稍后调⽤该宏:
PR("Howdy");
PR("weight = %d, shipping = $%.2fn", wt, sp);
对于第1次调⽤,_ VAARGS 展开为1个参数:"Howdy"。对于第2次调⽤, VAARGS 展开为3个参数:
"weight = %d, shipping = $%.2fn", wt, sp
因此,展开后的代码是:
printf("Howdy");
printf("weight = %d, shipping = $%.2fn", wt, sp);
程序variadic.c演⽰了⼀个⽰例,该程序使⽤了字符串的串联功能和#运算符。
// variadic.c -- variadic macros
#include
#include
#define PR(X, ...) printf("Message " #X ": " __VA_ARGS__)
int main(void)
c++中string的用法
{
double x = 48;
double y;
y = sqrt(x);
PR(1, "x = %gn", x);
PR(2, "x = %.2f, y = %.4fn", x, y);
return 0;
}
第1个宏调⽤,X的值是1,所以#X变成"1"。展开后成为:
print("Message " "1" ": " "x = %gn", x);
然后,串联4个字符,把调⽤简化为:
print("Message 1: x = %gn", x);
下⾯是该程序的输出:
Message 1: x = 48
Message 2: x = 48.00, y = 6.9282
记住,省略号只能代替最后的宏参数:
#define WRONG(X, ..., Y) #X #__VA_ARGS__ #y // won't work

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。