【C/C++】宏定义中 #、##、__VA_ARGS__和##__VA_ARGS__的作用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013554213/article/details/88850136

在看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);

猜你喜欢

转载自blog.csdn.net/u013554213/article/details/88850136