1、宏函数
宏函数和宏定义一样,只是在预处理阶段进行替换
表达式:#define name(parament-list) stuff
注:参数列表可带可不带,如果带的话,name和(parament-list)之间不能有空格
- 单语句表达式的实例
#define Add(x,y) ((x)+(y)) \\注意,对变量和表达式都加上句号,否则可能会出现逻辑错误
int a=1,b=2;
int sum=Add(a,b);
// 预处理阶段相当于等价替换为:
int sum=((a)+(b));
这里要注意的是,宏函数进行替换时,会将传入的参数直接替换到语句中,而不是把a赋值给x,然后用x执行。(只是文本替换)
- 多语句表达式的实例
#define Foo(x,y) x++;y++
//可以使用\来换行写
#define Foo(x,y) \
x++; \
y++
// 多语句表达式往往采用do{}while(0)将语句包含
#define Foo(x,y)
do \
{ \
x++; \
y++; \
}while(0)
对于多语句表达式,往往使用do{}while(0)语句将表达式包含,主要原因如下:
(1)、存在一个独立的block,语句被当作一个整体实现,示例:
#define Foo() \
printf("hello,"); \
printf("world")
if(a>b)
Foo();
//预处理替换后为:
if(a>b)
printf("hello,");
printf("world");
//这里就会出现逻辑错误,无论a>b是否成立,实际上printf("world")都会执行
(2)、对于上述示例,使用单独的{}也能实现,但是也会出现其他问题:
#define Foo(){ \
printf("hello,"); \
printf("world");}
if(a>b)
Foo();
else
printf("Others");
//预处理替换后为:
if(a>b)
{
printf("hello,");
printf("world");
}; //这里会多处一个分号,导致语句报错。
else
printf("Others");
// 如果使用的do{}while(0),预处理后如下,可以正常运行
if(a>b)
do
{
printf("hello,");
printf("world");
}while(0);
else
printf("Others");
2、关于可变参数
1、特殊符号#与##的意义:
单独一个“#”符号表示:用来把参数转换成字符串。即给传入的参数名加上""
#define string_1(x) #x
string str=string_1(Mstr);
// 如果没有使用#,则等价于
string str=Mstr;
// 如果使用#,则等价于
string str="Mstr";
“##”连接符号,是用于将两个标记连接成一个标记的运算符,也称为连接符。用于将参数连接起来,形成一个新的标记。这种方式可以在编译时将代码中的宏函数展开成实际的代码。
#define PRINT(x,y) x##y
int ab=2;
cout<<PRINT(a,b);
//等价于
cout<<ab;
- 可变参数__VA_ARGS__
// __VA_ARGS__ 宏前面加上##,当可变参数的个数为0时,这里的##起到把前面多余的逗号去掉的作用,否则会编译出错。
#define my_print1(fmt,...) printf(fmt, __VA_ARGS__)
my_print1("iiijjj\n") // 错误打印
my_print1("i=%d,j=%d\n",i,j) // 正确打印
#define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__)
my_print2("iiijjj\n") // 正确打印
my_print2("i=%d,j=%d\n",i,j) // 正确打印
3、c++可变参数
标准C/C++包含头文件stdarg.h,该头文件中定义了如下三个宏:
注:
(1)、arg_ptr为va_list型参数,va_list也是一个宏,其定义为typedef char * va_list,实质上是一char型指针。
(2)、prev_param为…前面的最后一个固定参数
1、va_start(arg_ptr, prev_param );
#define va_start(ap,v) (ap=(va_list)&v+_INTSIZEOF(v))
显而易见,其含义为将最后那个固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。
2、va_arg(arg_ptr,type);
va_arg宏的意思则指取出当前arg_ptr所指的类型为t可变参数并将ap指针指向下一可变参数
如va_arg(ap,int)就表示取出一个int数据并将指针向移四个字节。
3、va_end(arg_ptr);
将arg_ptr指针置成NULL,主要是为了代码对称
4、vfprintf()函数和vsprintf()函数
通过名字就可以看出来,这两个函数对于处理可变参数很有用
int vfprintf(FILE *stream,const char *format,va_list argptr);
int vsprintf(char *buffer,const char *format,va_list argptr);
void WriteLog(const char * format,...)
{
va_list va;
va_start(va,format);
vprintf(format,va);
va_end(va);
}
#define LOG_BASE(format,...)\
{ \
WriteLog(format,##__VA_ARGS__);\
}
int main()
{
LOG_BASE("--Login--user:%s,ID:%d","LiHua",12306);
};