#define简单妙用

简介: 1:__FILE__,__LINE__,宏展开,中间层宏 __FILE__是编译器预定义宏,表示此文件的绝对路径,但是ASCII字符。 假如你想将__FILE__转换成宽字符。

1:__FILE__,__LINE__,宏展开,中间层宏

__FILE__是编译器预定义宏,表示此文件的绝对路径,但是ASCII字符。

假如你想将__FILE__转换成宽字符。

或许你想这样

 

#define __WFILE__  L##__FILE__

wchar_t pszFilePath[]=__WFILE__;

 

这样的话编译器会提示说,“L__FILE__”: 未声明的标识符。

这是为什么?

如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开,也就是只替换一次。需要加一层中介完成代换,就想这样。

 

#define __WFILE3__(x) (L##x)
#define __WFILE2__(x) __WFILE3__(x)
#define __WFILE__  __WFILE2__(__FILE__)

wchar_t pszFilePath[]=__WFILE__;

 

当宏函数的定义中调用了另一个宏函数,预处理器会将其中所有可展开的宏展开。这样就可以了。


再看一个具有迷惑性的错误宏定义:

#define __WFILE1__(x) (L##x)

#define __WFILE__  __WFILE1__(__FILE__)

wchar_t pszFilePath[]=__WFILE__;


这样呢,编译器同样会报出L__FILE__未定义,也就是说L_FILE__不是一个字符串。

让我们一步一步看看预处理器是如何做的吧?

1.当预处理器到达wchar_t pszFilePath[]=__WFILE__;它发现宏定义,然后执行展开__WFILE1__( __FILE__ )

2.同时发现__FILE1__又是一个宏定义,那么展开(L##__FILE1__)


为什么预处理器没有展开__FILE1__呢,你可能觉得,自己已经添加了中间步骤。但实际上没有,我们单单看__WFILE1__( __FILE__ ),不关其他定义,你就会发现,这个宏定义根据前面的规则是绝对不会展开的,所以前面的__WFILE__根本没有起到中间代换的作用。


所以总结起来的话,从__FILE__第一次出现,必须要经过中间一层宏。中间一层宏是指__FILE__必须出现在宏声明中(宏的左侧),同时宏的定义(即宏定义右侧)也是一个宏。那么这个宏才能称为中间层。这个中间层才能起到展开__FILE__的作用。


 

下面看下个错误的字符串宏定义例子

 

#define SHOWLINENUMW3(x) (L##x)
#define SHOWLINENUMW2(x) SHOWLINENUMW3(#x)
#define SHOWLINENUMW(x)  SHOWLINENUMW2(x)
#define __WLINE__ SHOWLINENUMW(__LINE__)

 

#define __WFILE3__(x) (L##x)
#define __WFILE2__(x) __WFILE3__(x)
#define __WFILE__  __WFILE2__(__FILE__ )

 

#define __N__ (__WFILE__ __WLINE__)

 

TCHAR szFileName[]=__N__;

 

注意L##x是带括号的。编译器提示:项不会计算为接受 1 个参数的函数。

 

为什么会出错呢?那是因为对多个单独的字符串连接时,不能用括号括起来括起任意一个。当然单独一个可以。

如:

wchar_t szTemp[]=(L"nihao"); //第一种 可以

wchar_t szTemp[]=(L"nihao") (L"tahao");//第二种 不可以。

wchar_t szTemp[]=L"nihao" L"woyehao";//第三种 可以

 

而前面给出的错误的例子经过宏替换这后就相当于第二种,所以编译器会报告错误。我们需要将(L##x)改成L##x才能正确运行。

 

所以正确的例子是:

#define SHOWLINENUMW3(x) L##x
#define SHOWLINENUMW2(x) SHOWLINENUMW3(#x)
#define SHOWLINENUMW(x)  SHOWLINENUMW2(x)
#define __WLINE__ SHOWLINENUMW(__LINE__)

 

#define __WFILE3__(x) L##x
#define __WFILE2__(x) __WFILE3__(x)
#define __WFILE__  __WFILE2__(__FILE__ )

 

#define __N__ (__WFILE__ __WLINE__)

 

TCHAR szFileName[]=__N__;

2:__VA_ARGS__

__VA_ARGS__好像是c99标准加进来的宏定义功能,可能用于不变参数宏

#define MY_PRINTF( x,...) printf( x, ##__VA_ARGS__ )

其中...表示可变参数列表,__VA_ARGS__则是表示前面的可变参数列表,其中##用于在没可变参数列表时去掉前面的逗号。编译器会识别出##,并去掉前面的逗号。

如果没有##:

#define MY_PRINTF( x,...) printf( x, __VA_ARGS__ )

MY_PRINTF( "Hello World" );

宏展开后:

printf( "Hello World", );

这样的编译器会抱怨警告,甚至编译不过。采用##之后就可以了。而且##本身也是连接符。


当然有时我们可以直接:

#define MY_PRINTF(...) printf(__VA_ARGS__)

此时就不需要##,因为前面没有逗号。


暂时总结自己知道的这么多的,以后再补充吧。



相关文章
|
算法 Python
LightGBM高级教程:自动调参与超参数优化
LightGBM高级教程:自动调参与超参数优化【2月更文挑战第5天】
2112 2
|
自然语言处理 C++
探究C/C++编码世界:从字符编码到中文处理之艺(二)
探究C/C++编码世界:从字符编码到中文处理之艺
631 2
|
存储 人工智能 测试技术
图像相似度比较之 CLIP or DINOv2
图像相似度比较之 CLIP or DINOv2
|
8月前
|
存储 机器学习/深度学习 算法
|
关系型数据库 Linux 数据库
PostgreSQL源码编译安装
本节详细介绍了如何通过源码编译安装 PostgreSQL 17.6,涵盖从源码下载、依赖安装、配置编译参数、执行编译与安装、创建数据库用户与目录、初始化数据库,到配置 systemd 启动服务的完整流程。内容适用于多种 Linux 发行版,如 Rocky Linux、CentOS、openEuler、Ubuntu、Debian 等,并提供了常见错误的解决方法及一键安装脚本,帮助用户高效完成 PostgreSQL 的源码部署。
1114 0
PostgreSQL源码编译安装
|
弹性计算 运维 安全
无需注册、零广告!开源免费的运维面板Websoft9如何提升云端管理效率?
本文对比分析了包括Websoft9在内的五款无广告、免注册的开源运维面板,探讨它们在阿里云ECS等场景下的最佳实践。文章详细解析了各工具的功能特点、部署方式及安全加固方法,并通过实际案例展示了其在跨境电商站群管理和物联网数据中台等场景的应用价值,为开发者提供了一份全面的选型指南。
444 3
|
数据采集 数据可视化 数据挖掘
数据挖掘实战:使用Python进行数据分析与可视化
在大数据时代,Python因其强大库支持和易学性成为数据挖掘的首选语言。本文通过一个电商销售数据案例,演示如何使用Python进行数据预处理(如处理缺失值)、分析(如销售额时间趋势)和可视化(如商品类别销售条形图),揭示数据背后的模式。安装`pandas`, `numpy`, `matplotlib`, `seaborn`后,可以按照提供的代码步骤,从读取CSV到数据探索,体验Python在数据分析中的威力。这只是数据科学的入门,更多高级技术等待发掘。【6月更文挑战第14天】
1495 11
|
存储 缓存 监控
快速掌握Redis优化要点,告别性能瓶颈!
# Redis优化指南 了解如何提升Redis性能,从读写方式(整体与部分)、KV size、Key数量、读写峰值、命中率、过期策略、平均穿透加载时间、可运维性、安全性等方面着手。选择合适的读写策略,如只整体读写或部分读写变更,优化KV size避免过大或差异过大,合理管理Key数量,应对不同读写峰值,监控命中率并持续优化,设置智能过期策略,减少平均穿透加载时间,确保高可运维性并强化安全性。一起探索Redis的性能潜力!
3145 5
|
人工智能
实现广义相加模型GAM和普通最小二乘(OLS)回归
实现广义相加模型GAM和普通最小二乘(OLS)回归
|
域名解析 网络协议 安全
【域名解析 DNS 专栏】反向 DNS 解析:IP 地址到域名的映射秘密
【5月更文挑战第25天】反向DNS解析将IP地址转换为域名,用于验证和识别网络通信来源。它在邮件服务器验证、网络安全中扮演关键角色,例如检查发送邮件服务器的反向DNS以防止垃圾邮件。通过Python示例展示了反向DNS解析过程。尽管可能遇到错误配置和不准确信息,正确管理的反向DNS解析能增强网络的可信度和安全性。随着技术进步,反向DNS解析将持续优化,提升网络体验。
646 0