最近在开发过程中又玩起了C语言的可变参数,在资源受限的MCU平台上,一般不太建议使用可变参数或者相应标准库函数如printf等,以免编译进去后导致程序代码段瞬间变大很多,当然有些简化版本的lib,不过也相当废资源,不过现在MCU的性能越来越卷,大部分项目都是资源过剩。不过话说回来,C语言的可变参数用起来还挺顺手,像嵌入式经常打印的日志,为了更好的可移植性通常会进行封装,今天就不再赘述可变参数的语法了,大家感兴趣可以翻阅公众号往期文章,今天bug菌仅聊聊##__VA_ARGS__与__VA_ARGS__这对可变参数宏,相信大家在日常的编程中接触得倒不多,不过在大型开源软件中还是屡有见到,那么下面带大家分析分析:1__VA_ARGS__介绍__VA_ARGS__ 是C语言中的一个预定义宏,用于处理可变数量的参数列表。通常用在宏定义中,以便宏能够接受不定数量的参数,并将它们作为整体处理。在C语言中,使用函数可以接受可变数量的参数,例如printf和fprintf。然而,在宏定义中,参数数量通常是固定的。为了能够定义可以处理不同数量参数的宏,C99标准引入了__VA_ARGS__。下面的例子也都是在C99标准下编译运行。__VA_ARGS__表示所有在宏调用中传递的额外参数。它可以放在宏定义的参数列表的末尾,用于接收任意数量的额外参数,比如:#define DEBUG_LOG(fmt, ...) printf(fmt, __VA_ARGS__)
解析:DEBUG_LOG 是一个宏,接受至少一个参数 fmt(格式化字符串),后面可以是任意数量的参数。__VA_ARGS__ 表示可变参数列表,在宏展开时会被替换为传递给 DEBUG_LOG 宏的所有实际参数。示例:#define DEBUG_LOG(fmt, ...) \
printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
DEBUG_LOG("Hello, %s! The answer is %d\n", "world", 42);
在这个调用中,"Hello, %s! The answer is %d\n" 是格式化字符串fmt,"world" 和 42 是额外的参数。这些参数会被依次传递给 printf 函数。__VA_ARGS__ 必须总是与宏定义中的省略号 ... 一起使用。省略号...用于指示宏可以接受可变数量的参数。在宏定义中,__VA_ARGS__ 可以单独使用(如果没有额外参数时),也可以与其他参数一起使用。2##在前面有啥用?大家在了解__VA_ARGS__时,一定会看到有些地方在该宏定义前使用 ## 运算符来处理可变参数,如:#define DEBUG_LOG(fmt, ...) \
printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
其中这里的##运算符主要是用于连接左侧的符号(通常是逗号)和右侧的参数,从而确保宏展开时语法正确。对于如下使用可变参数:
DEBUG_LOG("Value of x: %d", 42);
__VA_ARGS__与##__VA_ARGS__是没有太大区别的,且都能编译通过,打印输出相同的结果。然而当可变参数为空时,如下:DEBUG_LOG("Program starting...");
在C99编译器下,无##修饰的宏定义DEBUG_LOG 则会编译失败,而存在##修饰的宏##__VA_ARGS__ 处理为空的可变参数列表,防止在宏展开时出现不必要的逗号,从而使得编译通过。 |