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
相关推荐
LaoWaiHang1 分钟前
C语言从头学61——学习头文件signal.h
c语言
一名路过的小码农43 分钟前
C/C++动态库函数导出 windows
c语言·开发语言·c++
m0_631270401 小时前
标准c语言(一)
c语言·开发语言·算法
万河归海4281 小时前
C语言——二分法搜索数组中特定元素并返回下标
c语言·开发语言·数据结构·经验分享·笔记·算法·visualstudio
小周的C语言学习笔记1 小时前
鹏哥C语言36-37---循环/分支语句练习(折半查找算法)
c语言·算法·visual studio
凌肖战2 小时前
力扣上刷题之C语言实现(数组)
c语言·算法·leetcode
Jhxbdks3 小时前
C语言中的一些小知识(二)
c语言·开发语言·笔记
代码雕刻家3 小时前
数据结构-3.1.栈的基本概念
c语言·开发语言·数据结构
AlexMercer10123 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
Reese_Cool3 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言