写在前面
简单总结一下C语言中x-macros(宏)的几种主要用法, 虽然主流的C语言程序中一般是不推荐使用宏的, 但是作为一种不错的C语言语法糖, 了解一下也不错,至少能看懂点大佬的代码. 主要参考了C语言程序设计:现代方法第十四章预处理器
.
基本语法
直接展开
其实就是简单的展开而已.
#include <stdio.h>
#define n1 100
#define M (n1*n1+3*n1)
int main() {
int sum;
sum = 3 * M + 4 * M + 5 * M;
printf("sum=%d\n", sum);
return 0;
}
这段程序中就是一个预处理器(宏)的简单用法, 直接进行简单的(无脑)替换操作.
带参数的宏
#include <stdio.h>
#define printn(n) printf("n=%d\n", n)
int main() {
int sum = 20 + 12;
printn(sum);
return 0;
}
井号的用途
单个井号表示变量替换, 后面跟上要替换的变量; 两个井号挨着表示连接.
#include <stdio.h>
#include <stdlib.h>
void t1() {
// #表示变量替换
#define AREA(x,y) printf("长为"#x",宽为"#y"的长方形面积为%d.\n",(x)*(y));
int i = 55, j = 3;
AREA(i, j);
AREA(3, 2);
/*
长为i,宽为j的长方形面积为165.
长为3,宽为2的长方形面积为6.
*/
}
//##表示连接
#define VAR(n)v##n
int v1(int a, int b) {
return a + b;
}
int v2(int a, int b) {
return a - b;
}
void t2() {
printf("%d\n", VAR(1)(2, 3));
printf("%d\n", VAR(2)(2, 3));
/*
5
-1
*/
}
int main(int argc, char const *argv[]) {
// t1();
t2();
return 0;
}
预定义的宏
C语言中预定义了一些宏, 可以用于调试代码.
名称 | 描述 |
---|---|
__LINE__ |
被编译的文件中的行号 |
__FILE__ |
被编译的文件名称 |
__DATE__ |
编译日期(mm dd yyyy) |
TIME |
编译时间((hh:mm:ss)) |
__STDC__ |
编译器符合C标准(89or99),值为1 |
__func__ |
存储当前正在执行的函数的名字的字符串变量 |
这里直接用之前写好的C++代码了,C也一样,改成printf即可.
#include <iostream>
using namespace std;
#define LOG(x) (cout<<__FILE__<<" "<<__LINE__<<": "<<__PRETTY_FUNCTION__<<(x)<<endl)
void t1() {
LOG("hello");
}
template<typename T>
void t2(T s) {
LOG(s);
cout << __TIME__ << endl;
cout << __DATE__ << endl;
cout << __func__ << endl;
/*
gcc_pre_define.cc 13: void t2(T) [with T = char]1
00:17:03
Aug 24 2022
t2
*/
}
int main(int argc, char const *argv[]) {
// t1();
t2('1');
return 0;
}
任意个数的参数的宏(C99)
#include <stdio.h>
#include <stdarg.h>
// 测试函数的用法是: 满足参数条件的话, 执行打印"通过测试", 否则执行自定语句
#define TEST(cond, ...) \
((cond) ? printf("Passed test: %s\n", #cond) : \
printf(__VA_ARGS__))
void t1() {
int v = 10, maxV = 20;
TEST(v <= maxV, "v %d exceeds %d\n", v, maxV);
v = 100, maxV = 20;
TEST(v <= maxV, "v %d exceeds %d\n", v, maxV);
/*
Passed test: v <= maxV
v 100 exceeds 20
*/
}
int main(int argc, char const *argv[]) {
t1();
return 0;
}
…记号(省略号)出现在宏参数列表的最后,前面是普通参数。__VA_ARGS__是一个专用的标识符,只能出现在具有可变参数个数的宏的替换列表中,代表所有与省略号相对应的参数。
(至少有一个与省略号相对应的参数,但该参数可以为空。)宏TEST至少要有两个参数,第一个参数匹配condition,剩下的参数匹配省略号。
注意事项
- 在每一个宏替换的变量两边都要加爱上圆括号, 这样可以防止宏替换之后的代码意义改变.
- 多行的宏定义, 在断行位置需要加上反斜杠(转义);
- 单一的井号后接变量名, 这种用法放在printf中, 隐含了C语言字符串字面量可以直接合并.
更多细节请看上面提到的书, 对一些比较特殊的例子也有解释.