C语言参悟-文本替换宏

C语言参悟-文本替换宏

一、概述

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

(1) 方便程序的修改

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

(2) 提高程序的运行效率

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

二、语法

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

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

cpp 复制代码
#define PI 3.1425926

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

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

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

例如:

cpp 复制代码
#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. # 运算符号

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

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

cpp 复制代码
#define showlist(...) puts(#__VA_ARGS__)
showlist();            // 展开成 puts("")
showlist(1, "x", int); // 展开成 puts("1, \"x\", int")

2. ## 运算符

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

cpp 复制代码
#define Conn(x,y) x##y
cpp 复制代码
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语言的,FILELINE 可以输出日志去定位到某一行代码

一个简单的例子如下:

cpp 复制代码
// 定义
#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
相关推荐
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
我是谁??1 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
希言JY2 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
午言若2 小时前
C语言比较两个字符串是否相同
c语言
TeYiToKu3 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
互联网打工人no14 小时前
每日一题——第一百二十四题
c语言
爱吃生蚝的于勒4 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~4 小时前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
洋2404 小时前
C语言常用标准库函数
c语言·开发语言
徐嵌5 小时前
STM32项目---畜牧定位器
c语言·stm32·单片机·物联网·iot