在看linux内核kernel.h头文件时,看到这样一个语句:
#define pr_err(fmt, ...) \
eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
于是来百度一下,这里也做个笔记。
1 #
用来把参数转换成字符串
#用在预编译语句里面可以把预编译函数的变量直接格式成字符串;
示例1
// testa.c
#include<stdio.h>
#include <stdlib.h>
#define Func1(x) printf("the square of " #x " is %d.\n",(x)*(x))
int main(void)
{
int aa=30;
Func1(aa);
Func1(30);
system("pause");
return 0;
}
运行结果
the square of aa is 900.
the square of 30 is 900.
注意,不能直接在其它非预编译函数
直接使用#aa的形式,假如main函数里面出现printf(“the square of " #x " is %d.\n”,(x)*(x))是不能通过编译的。
2 ##
是宏连接符,作变量链接
##运算符把两个语言符号组合成单个语言符号,为宏扩展提供了一种连接实际变元的手段
示例2
// testb.c
#include<stdio.h>
#include <stdlib.h>
#define Func3(a) printf("the square of " #a " is %d.\n",b##a)
int main(void)
{
int m=30;
int bm=900;
Func3(m); //展开后相当于 printf("the square of m is %d.\n",bm);
system("pause");
return 0;
}
运行结果
the square of m is 900.
##就是个粘合剂,将前后两部分粘合起来,也就是有“字符化”的意思。但是“##”不能随意粘合任意字符,必须是合法的C语言标示符。在单一的宏定义中,最多可以出现一次“#”或“##”预处理操作符。如果没有指定与“#”或“##”预处理操作符相关的计算次序,则会产生问题。为避免该问题,在单一的宏定义中只能使用其中一种操作符(即,一份“#”或一个“##”,或都不用)。除非非常有必要,否则尽量不要使用“#”和“##”
3 __VA_ARGS__
是可变参数宏
用法如下:
#define Debug(...) printf(__VA_ARGS__)
使用的时候只需要:
Debug("Y = %d\n", y);
此时编译器会自动替换成printf("Y = %d\n", y");
4 ##__VA_ARGS__
也是可变参数宏
##__VA_ARGS__
宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错
示例3
#define my_print1(...) printf(__VA_ARGS__)
#define my_print2(fmt,...) printf(fmt, __VA_ARGS__)
my_print1("i=%d, j=%d\n",i,j)
正确打印
my_print1("iiiiiii\n")
编译失败打印,因为扩展出来只有一个参数,至少要两个及以上参数
如果是
#define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__)
那么,my_print1
里面不管是几个参数都能正确打印
5 应用实例
5.1 宏定义
// 是否打印信息
#define INFO(fmt, ...) \
if (enable_verbose) \
printf("INFO: %s(): (line:%d) " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);
// 警告信息
#define WARN(fmt, ...) \
printf("WARN: %s(): (line:%d) " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);
// 检查返回值是否出错
#define CHECK_ERROR(cond, label, fmt, ...) \
if (!cond) { \
error = 1; \
printf("ERROR: %s(): (line:%d) " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto label; \
}
// 返回出错信息
#define ERROR_RETURN(fmt, ...) \
do { \
printf("ERROR: %s(): (line:%d) " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return false; \
} while(0)
5.2 使用示例
INFO("Initialize v4l2 components successfully");
or
INFO("Camera ouput format: (%d x %d) stride: %d, imagesize: %d",
width, height, bytesperline, sizeimage);
WARN("The desired format is not supported");
// 检查parse_cmdline()函数的返回值是否为false,出错即跳转至cleanup
CHECK_ERROR(parse_cmdline(&ctx, argc, argv), cleanup, "Invalid options specified");
if (ret == -1)
ERROR_RETURN("Failed to read device %s: %s (%d)", devname, strerror(errno), errno);