欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > C语言参悟-文本替换宏

C语言参悟-文本替换宏

2024/11/30 12:35:16 来源:https://blog.csdn.net/qq_43680827/article/details/135652891  浏览:    关键词:C语言参悟-文本替换宏

C语言参悟-文本替换宏

  • 一、概述
  • 二、语法
    • 1. # 与 ## 运算符
      • 1. # 运算符号
      • 2. ## 运算符
  • 三、预定义宏

一、概述

这里记录一下,C,C++中预处理器中的文本宏替换和仿函数文本宏替换。宏替换用的非常多,有非常多的优点。

(1) 方便程序的修改
因为宏的本质就是替换,我们只需要改一处,其他的宏修改也会被同时修改好,这就便于维护管理。

(2) 提高程序的运行效率
使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率有点内联函数的味道,但是如果是复杂的宏。还是用函数好点儿。

二、语法

#define 标识符 替换列表(可选) (1)  #define 标识符( 形参 ) 替换列表(可选) (2)  #define 标识符( 形参, ... ) 替换列表(可选) (3) (C++11) #define 标识符( ... ) 替换列表(可选) (4) (C++11) #undef 标识符 (5) 

#define 指令将 标识符 定义为宏,即指示编译器以将其后出现的所有 标识符 都替换为 替换列表,而它也可以被进一步处理

#define PI 3.1425926

仿对象宏 : 将 替换列表 替换每次出现的被定义 标识符。#define 指令的版本 (1) 准确表现如此。

仿函数宏 : 将 替换列表 替换每次出现的被定义 标识符,可选地接受一定量的实参,它们随即替换掉 替换列表 中出现的任何对应的 形参

仿函数宏语法类似函数调用语法:每个宏名实例后随一个 ( 作为下个预处理记号,所引入的记号序列将被替换为 替换列表。该序列以匹配的 ) 记号终止,跳过中间的匹配左右括号对。

例如:

#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)
#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
F(a, b, c) // 替换为 f(0, a, b, c)
F()        // 替换为 f(0)
G(a, b, c) // 替换为 f(0, a, b, c)
G(a, )     // 替换为 f(0, a)
G(a)       // 替换为 f(0, a)
SDEF(foo);       // 替换为 S foo;
SDEF(bar, 1, 2); // 替换为 S bar = { 1, 2 };

1. # 与 ## 运算符

1. # 运算符号

仿函数宏中,如果替换列表 中一个标识符前有 # 运算符,那么该标识符在运行形参替换的基础上以引号包围,实际上创建一个字符串字面量,也就是把宏参数替换为字符串的形式。

另外,预处理器为内嵌的字符串字面量(如果存在)外围的引号添加反斜杠以进行转义,并按需要双写字符串中的反斜杠。移除所有前导和尾随空白符,并将文本中间(除内嵌字符串字面量中间外)的任何空白符序列缩减成单个空格。此操作被称为“字符串化”,如果字符串化的结果不是合法的字符串字面量,那么行为未定义。

#define showlist(...) puts(#__VA_ARGS__)
showlist();            // 展开成 puts("")
showlist(1, "x", int); // 展开成 puts("1, \"x\", int")

2. ## 运算符

如果替换列表 中任何两个相继标识符之间有 ## 运算符,那么这两个标识符(首先未被宏展开)在运行形参替换的基础上将结果进行拼接。此操作被称为“拼接”或“记号粘贴”。只有一同组成合法记号的记号才可以粘贴:如组成更长标识符的标识符、组成数字的数字位,或组成 += 的运算符 + 和 =。

#define Conn(x,y) x##y
int n = Conn(123,456); /* 结果就是n=123456;*/
char* str = Conn("asdf", "adf"); /*结果就是 str = "asdfadf";*/

三、预定义宏

这里记录一下常用的宏,

宏名含义
__cplusplus代表所用的 C++ 标准版本,值为:199711L(C++11 前),201103L(C++11),201402L(C++14),201703L(C++17),202002L(C++20),202302L(C++23)
__FILE__展开成当前文件名,作为字符串字面量,可用 #line 指令更改
__LINE__展开成源文件行号,整数常量,可用 #line 指令更改
__DATE__展开成翻译日期,形式为 “Mmm dd yyyy” 的字符串。如果月中日期数小于 10 则 “dd” 的首字符为空格。月份名如同以 std::asctime() 生成
__TIME__展开开成翻译时间,形式为 “hh:mm:ss” 的字符串字面量

就像 __cplusplus 可以去区分是否是C++还是C语言的,__FILE__、__LINE__ 可以输出日志去定位到某一行代码

一个简单的例子如下:

// 定义
#define INFO "info"
#define DEBUG "debug"
#define LOG(RANK, LOGDATA) \
std::cout<<"["<<RANK<<" "<<__DATE__<<" "<<__TIME__<<"] "<<__FILE__<<" line "<<__LINE__<<" : "<<LOGDATA<<std::endl;// 使用
LOG(INFO, "hello world!");
LOG(DEBUG, "i am world");// 输出
[info Aug 13 2024 14:39:02] main.cpp line 38 : hello world!
[debug Aug 13 2024 14:39:02] main.cpp line 39 : i am world

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com