一、一处修改,全局生效
#define PI ((double)3.1415926)
对于圆周率这种特殊的值,可以使用宏值替换,方便以后对于精度的修改等。
二、简化代码
#define MAX_VAL(v1, v2) ((v1) > (v2) ? (v1) : (v2))
上面的宏用于获取两个值里面的较大值,在定义的宏里面,必要的括号是不可少的;虽然上面的宏加上了很多括号,但是在下面的语句中仍然会存在问题,虽然也有其他方式可以解决这样子的问题,但是在编码过程中还是不建议在宏里面出现这样子的操作,包括一些函数调用。
MAX_VAL(a++, b++)
三、宏中使”{}”
#define SWAP_INT(v1, v2)\
{\
int tmp = 0;\
tmp = v1;\
v1 = v2;\
v2 = tmp;\
}
上面的宏的作用是交换两个int的值,在定义的宏中涉及多条语句时,需要使用”{}”,这样子可以将多条语句作为一个代码块实现。
四、宏定义用”do{ }while(0)”
if (v1 < v2)
SWAP_INT(v1, v2);
else
printf("swap_int : v1 = %d, v2 = %d.\n", v1, v2);
上面的语句使用SWAP_INT宏,编译时报错,因为else没有对应的if。可以修改成下面的形式,编译时就不会发生错误了。
#define SWAP_INT(v1, v2)\
do{\
int tmp = 0;\
tmp = v1;\
v1 = v2;\
v2 = tmp;\
}while(0)
采用这样子好处是可以避免分号带来的麻烦,类似定义了一个代码块,可以实现一个较复杂的功能,并且不会与上下文混淆。类似的宏经常在代码中出现来提升代码的健壮性。
五、宏定义用”#”
在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组。
#define ERROR_LOG(module) fprintf(stderr, "error: "#module"\n")
#define PRINT_PARAM(param) printf("%s=%d\n",#param, param)
下面的语句执行的效果如下:
ERROR_LOG(max_val); -> error: max_val
ERROR_LOG("v1 = 0, v2 = 10"); -> error: "v1 = 0, v2 = 10"
PRINT_PARAM(v1); -> v1=10
PRINT_PARAM(v1 + v2); -> v1 + v2=10
六、宏定义用”##”
“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。
在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。
#define STR_CAT(src1, src2) (src1 src2) //GCC使用##连接字符串编译报错替换成空格即可
#define CAT_PARAM(p1, p2) (p1##p2)
#define TYPE1(type,name) type name_##type##_type
#define TYPE2(type,name) type name##_##type##_type
下面的语句执行的效果如下:
int num = CAT_PARAM(12, 32); -> num = 1232
char *pDst = STR_CAT("acb", "def"); -> pDst = acbdef
TYPE1(int, c); -> int name_int_type ; (因为##号将后面分为 name_ 、type 、 _type三组,替换后强制连接)
TYPE2(int, d); -> int d_int_type ; (因为##号将后面分为 name、_、type 、_type四组,替换后强制连接)
七、宏定义用typeof
typeof 需要头文件<stddef.h>,它经常出现在宏定义中用来获得变量的类型,如下:
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
这里主要说明一下:
(void) (&_max1 == &_max2);
这一句很有意思:看起来是一句废话,其实用得很巧妙!它主要是用来检测宏的两个参数 x 和 y 的数据类型是否相同。如果不相同,编译器会给一个警告信息,提醒程序开发人员。
warning:comparison of distinct pointer types lacks a cast
八、可变参数宏 … 和 _ VA_ARGS _
#define PRINT(...) printf(__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, args) // args...可以理解为给可变参数...起别名,否则使用__VAR_ARGS__
LOG("%s","123"); //正确
LOG("123"); //错误 展开后,相当与LOG(,"123")
#define LOG(format, args...) fprintf(stdout, format, ##args)
LOG("%s","123"); //正确
LOG("123"); //正确 ##加上后不会进行连接。
##”连接符号的用法,“##”的作用是对token进行连接,上例中format,args都可以看作是token,如果token为空,“##”则不进行连接,所以允许省略可变参数 因为format这个token为空,又因为有##的加持,所以不连接。
##这个连接符号充当的作用就是当__VAR_ARGS__或者args为空的时候,消除前面的那个逗号
九、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用’#‘或’##'的地方宏参数是不会再展开,例如:
#define STR(s) #s
printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #include<climits>
这行会被展开为:
printf("int max: %s\n", "INT_MAX");
参考网址
https://www.cnblogs.com/alantu2018/p/8465911.html
https://zhuanlan.zhihu.com/p/55771861
https://www.cnblogs.com/hnrainll/archive/2012/08/15/2640558.html