【C语言 | 预处理】举例讲解操作符(#、##)与可变参数(__VA_ARGS__、args)

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍C语言预处理的 #、##、__VA_ARGS__、##args🍭
😎金句分享😎:🍭🍭

本文未经允许,不得转发!!!


在这里插入图片描述

🎄一、概述

#、##、##__VA_ARGS__、##args 这几个符号在C语言自定义的宏表达式中,常常会看到,本文介绍这个符号的作用,以及列举几个简单例子,直观了解这几个符号的使用。

操作符作用
#将宏参数转换为字符串常量
##把多个语言符号组合成单个语言符号
__VA_ARGS__表示可变参数列表
args表示可变参数列表

在这里插入图片描述

🎄二、#操作符

C语言中,#操作符的作用是,将宏参数转换为字符串常量。

注意#操作符只会将 宏参数 转成字符串,在宏定义中,把#接在其他符号前并不会将该符号变成字符串,只有接在 宏参数 前,变成字符串。

✨2.1 举例说明

用法参考下面代码:

// preDel#.c
// gcc preDel#.c -E -o preDel#.i  //预编译
// gcc preDel#.c    // 编译
#include <stdio.h>
#define MY_STR(x) #x
#define SQR(x) printf("The square of "#x" is %d.\n", ((x)*(x)));
#define CALL(fun) (printf("CALL %s\n", #fun), fun())
void function()
{
}
int main(void)
{
	printf("%s\n", MY_STR(Hello world!));
	SQR(8);
	CALL(function);	// 先打印函数名,再调用函数 
	return 0;
}

编译后,运行结果如下:
在这里插入图片描述

✨2.2 查看预处理文件

保存上面代码,执行gcc preDel#.c -E -o preDel#.i,然后查看preDel#.i的最后几行,可以看到#操作符是怎么替换的,替换后代码如下:

# 5 "preDel#.c" 2

void function()
{

}
int main(void)
{
 printf("%s\n", "Hello world!");
 printf("The square of ""8"" is %d.\n", ((8)*(8)));;
 (printf("CALL %s\n", "function"), function());
 return 0;
}

从预处理的结果来看,宏函数里的字样#宏参数 都会被加上双引号替换成 "#宏参数" 的字符串。当我们需要在宏表达式里使用到宏参数的字符串时,就可以使用这个#操作符。

在这里插入图片描述

🎄三、##操作符

##操作符 的作用是,把多个语言符号组合成单个语言符号。

  • 使用方式1:##在宏定义中,连接多个符号;
  • 使用方式2:##在宏定义中,连接一个固定符号到宏参数;
  • 使用方式3:##在宏定义中,连接多个宏参数;
  • 使用方式4:##在宏定义中,与__VA_ARGS__、args宏一起使用。

注意##连接后仍然是语言符号,不字加了双引号的字符串。

✨3.1 举例说明

用法参考下面代码:

#include <stdio.h>
#define CONNECT_TWO_SYBBOL      abc##def        // 连接两个符号为一个 adbdef
#define CONNECT_THREE_SYBBOL    abc##def##ghi   // 连接三个符号为一个 adbdefghi
#define CONNECT_SYMBOL_PARAM(x) char g_c##x     // 连接符号和宏参数
#define CONNECT_PARAMS(x,y,z)   g_##x##y##z     // 连接多个宏参数
int main(void)
{
	int CONNECT_TWO_SYBBOL;
	int CONNECT_THREE_SYBBOL;
	CONNECT_SYMBOL_PARAM(x);
	int CONNECT_PARAMS(a,b,c);
	
	return 0;
}

✨3.2 查看预处理文件

保存上面代码,执行gcc preDel##.c -E -o preDel##.i,然后查看preDel##.i的最后几行,可以看到##操作符是怎么替换的,替换后代码如下:

# 2 "preDel##.c" 2

int main(void)
{
 int abcdef;
 int abcdefghi;
 char g_cx;
 int g_abc;

 return 0;
}

从预处理的结果来看,##操作符将多个符号连接成一个符号。
在这里插入图片描述

🎄四、__VA_ARGS__ 宏(可变参数)

C 语言中 __VA_ARGS__ 是一个可变参数的宏,是新的 C99 规范中新增的,需要配合 #define 使用。
__VA_ARGS__的作用是,将左边宏表达式中可变参数 (…) 的内容原样抄写在右边 __VA_ARGS__ 所在的位置。

注意:如果宏表达式的参数可能为空的话,需要在__VA_ARGS__前面加##,把多余的逗号 , 去掉,否则会编译出错。

✨4.1 举例说明

用法参考下面代码,LOG("no param\n");这句会出错,编译时要注释掉:

// preDel_VA_ARGS.c
// gcc preDel_VA_ARGS.c -E -o preDel_VA_ARGS.i  //预编译
// gcc preDel_VA_ARGS.c    // 编译
#include <stdio.h>
#define __TXT_(name, ...) char* g_txt_##name[]={__VA_ARGS__}
#define LOG(fmt,...)  printf(fmt,__VA_ARGS__)
#define DEBUG_PRINT(fmt, ...) \
    printf("[%s:%d] " fmt , __FILE__, __LINE__, ##__VA_ARGS__)
int main(void)
{
	__TXT_(str, "this is str1", "this is str2");
	LOG("str1=[%s]\n",g_txt_str[0]);
	LOG("no param\n"); // 这里没有参数,会编译出错
	DEBUG_PRINT("str2=[%s]\n",g_txt_str[1]);
	DEBUG_PRINT("no param\n"); // 这里没有参数,不出错
	return 0;
}

✨4.2 查看预处理文件

保存上面代码,执行gcc preDel_VA_ARGS.c -E -o preDel_VA_ARGS.i,然后查看preDel_VA_ARGS.i的最后几行,可以看到__VA_ARGS__是怎么替换的,替换后代码如下:

# 2 "preDel_VA_ARGS.c" 2

int main(void)
{
 char* g_txt_str[]={"this is str1", "this is str2"};
 printf("str1=[%s]\n",g_txt_str[0]);
 printf("no param\n",);	// __VA_ARGS__前没加##,会多出逗号,导致编译出错
 printf("[%s:%d] " "str2=[%s]\n" , "preDel_VA_ARGS.c", 11,g_txt_str[1]);
 printf("[%s:%d] " "no param\n" , "preDel_VA_ARGS.c", 12);// __VA_ARGS__前加了##,不会多出逗号
 return 0;
}

在这里插入图片描述

🎄五、args(可变参数)

args也是一个宏。它的作用与__VA_ARGS__几乎一致,将左边宏表达式中可变参数 (args…) 的内容原样抄写在右边 args 所在的位置。

✨5.1 举例说明

用法参考下面代码,把__VA_ARGS__例子的代码,改成使用argsLOG("no param\n");这句会出错,编译时要注释掉:

// preDel_args.c
// gcc preDel_args.c -E -o preDel_args.i  //预编译
// gcc preDel_args.c    // 编译
#include <stdio.h>
#define __TXT_(name, args...) char* g_txt_##name[]={args}
#define LOG(fmt, args...)  printf(fmt,args)
#define DEBUG_PRINT(fmt, args...) \
    printf("[%s:%d] " fmt , __FILE__, __LINE__, ##args)
int main(void)
{
	__TXT_(str, "this is str1", "this is str2");
	LOG("str1=[%s]\n",g_txt_str[0]);
	LOG("no param\n"); // 这里没有参数,会编译出错
	DEBUG_PRINT("str2=[%s]\n",g_txt_str[1]);
	DEBUG_PRINT("no param\n"); // 这里没有参数,不出错
	return 0;
}

✨5.2 查看预处理文件

保存上面代码,执行gcc preDel_args.c -E -o preDel_args.i,然后查看preDel_args.i的最后几行,可以看到args是怎么替换的,替换后代码如下:

# 2 "preDel_args.c" 2

int main(void)
{
 char* g_txt_str[]={"this is str1", "this is str2"};
 printf("str1=[%s]\n",g_txt_str[0]);
 printf("no param\n",);
 printf("[%s:%d] " "str2=[%s]\n" , "preDel_args.c", 11,g_txt_str[1]);
 printf("[%s:%d] " "no param\n" , "preDel_args.c", 12);
 return 0;
}

从预处理后的结果来看,args的处理结果和__VA_ARGS__的处理结果是一样的。

在这里插入图片描述

🎄六、总结

本文详细介绍了 #操作符、##操作符、__VA_ARGS__宏、args宏 的作用、用法、例子。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

引用:建议使用__VA_ARGS__时与##配合使用;##__VA_ARGS__在设置log输出的宏定义时比较常见。 引用:程序开发或者看工程代码时可能在宏定义中有涉及到#、##、__VA_ARGS__和##__VA_ARGS__的使用,简单介绍下使用方法。#操作符是将其后面紧跟的符号变为字符串的形式。 #__VA_ARGS__是C语言宏定义中的一个特殊标识符,表示可变参数列表。它可以用来在宏定义中传递不定数量的参数。当我们在宏定义中使用#__VA_ARGS__时,它会被替换为传递给宏的所有参数,以逗号分隔的形式。这样,我们可以在宏定义中使用这些参数,类似于函数的参数。 而##__VA_ARGS__是在宏定义中使用可变参数列表时常见的形式。它的作用是在宏定义中使用可变参数列表时,可以将参数列表前的逗号省略掉。这样可以避免在宏定义中使用可变参数列表时出现额外的逗号。 所以,#__VA_ARGS__和##__VA_ARGS__都是在宏定义中使用可变参数列表时的常见用法,可以实现灵活的宏定义,提高代码的可读性和可维护性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [# ## __VA_ARGS__ ##__VA_ARGS__的使用说明](https://blog.csdn.net/hanxv_1987/article/details/106555870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值