宏¶
1 宏对象 (object-like-macro)¶
- 使用场景:
- 宏对象常用于定义常量和类型别名
- 宏函数也能定义不带参数的函数,结构 (但这意义不大)
#define PI 3.14
#define FLOAT_64 double
#define CLASS_B class B{};
CLASS_B
#undef CLASS_B
int main(){
B b;
FLOAT_64 f64=PI;
}
2 宏函数 (Function-like-macro)¶
- 使用场景:
- 宏函数常用于定义表达式
- 宏函数也可定义带参数函数,甚至用来定义模板结构体
//宏函数定义表达式
#define EQUAL(a,b) ((a)==(b))
//宏函数定义模板类
#define CLASS_A(X) template<class T=X> \
class A{\
using VALUE_TYPE=T;\
public:\
VALUE_TYPE value;\
};
CLASS_A(int)
#undef CLASS_A
int main(){
A<> as1{2};
A<double> as2{ 1.1 };
EQUAL(1,2);
}
3 可变函数宏¶
3.1 使用__VA_ARGS__ 宏¶
- 可变参数表示,有 2 种方式,分为无名、别名:
- 无名可变参数,用
...
表示,如:#define debug(...) printf(__VA_ARGS__)
- 可变参数别名,如:
#define debug(args...) printf(args)
- 无名可变参数,用
- 可变参数展开,也按无名、别名来区分:
- 无名可变参数展开,用
__VA_ARGS__
展开。(示例见上面) - 可变参数别名, 直接使用参数别名即可。(示例见上面)
- 无名可变参数展开,用
- 拼接符
##
解决可变参数无参传入,可变参数参数前逗号,
导致报错问题,如:
#include <stdio.h>
// 没有##arg,当使用debug("hello, world");时,会展开为printf("hello, world",);导致报错
#define debug(format, args...) printf(format, ##args)
int main(void)
{
int year = 2018;
debug("hello, world"); //只有format参数,没有args可变参数
}
- 示例:利用宏函数处理 jni 种 C 和 C++相同接口不同调用规则问题
#ifdef __cplusplus
#define JNIEnv_FUNCTION_ADAPTER(fun, env, args...) env->fun(args);
#else
#define JNIEnv_FUNCTION_ADAPTER(fun, env, args...) (*env)->fun(env, ##args);
4 字符串化 (Stringizing)¶
- 注意事项:
- 当宏使用
#
时,不能将宏展开,如下例中str(foo)
不会展开宏foo
,因为#
会直接将foo
字符串化。当这种情况时,可以借助另一层宏 (不含#
) 来展开,如下例中xstr
宏可以先将foo
展开。
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
→ "foo"
xstr (foo)
→ xstr (4)
→ str (4)
→ "4"
5 拼接 (Concatenation)¶
- 注意事项
##
先起到分割标识的作用,再拼接其它符号,如下例中CON_A2
把NAME_
当成固定编码,而##NAME
会被识别为a
且和前面NAME_
拼接在一起,转换为NAME_a
#define CON_A1(NAME) int NAME_NAME
#define CON_A2(NAME) int NAME_##NAME
#define CON_A3(NAME) int NAME
#define CON_A4(NAME) int NAME##_NAME
#define CON_A5(NAME) int NAME##_##NAME
int main() {
CON_A1(a);//->NAME_NAME
CON_A2(a);//->NAME_a
CON_A3(a);//->a
CON_A4(a);//->a_NAME
CON_A5(a);//->a_a
}
6 预设宏¶
7 用于调试日志相关宏¶
__FILE__
: 显示当前文件名,如/usr/local/include/myheader.h
__LINE__
: 显示所在行号, 如100
__FUNCTION__
或__func__
: 显现所在函数名__DATE__
: 显示当前时期,如Feb 12 1996
__TIME__
: 显示当前时间,如23:59:01
7.1 版本相关宏¶
__cplusplus
: 使用 c++编译器,就会定义这个宏,不同版本值如下
- 199711L(C++11 前)
- 201103L(C++11)
- 201402L(C++14)
- 201703L(C++17),或
- 202002L(C++20)
(宏常量)
8 修改行号或文件名¶
- 语法:
#line linenum filename
- 示例
#include <stdio.h>
#line 100 "macro-new.c" // 更改下一行的行号和源文件名
int main() // line 100
{
printf("%d\n", __LINE__); // line 102
printf("%d\n", __LINE__); // line 103
printf("%s\n", __FILE__);
return 0;
}
- 输出
102
103
macro-new.c