前言
一些经常使用的简单函数,我们会把他定义为宏函数,宏函数确实具有一些优势,本文将讲述宏函数的使用方法和重点踩坑。
- 使用方便
- 提高系统运行效率:宏函数并不是真正的函数,只是把语句嵌在其他函数内,所以宏函数并不需要函数调用。
1. 宏函数和内联函数的区别
未了解以前,我感觉这两者似乎没有什么区别,就是书写方式不同而已。但有了了解以后,感觉这两者还是颇有差距的。
- 宏函数在预处理阶段就会展开,而内联函数在编译时才会展开(展现)
- 编译器在处理内联函数会优化,可能不会按我们的需求进行展开.
- 其他比如调试方面的差异.
2. 宏函数书写方式
2.1 一行宏函数
可以实现一些简单的取最小值、最大值、打印、计数等功能。
- 最小值(该方式仍有问题,后续解决)
#define MIN(X,Y) ((X)<(Y)?(X):(Y))
- 打印
#define PRINT(str) printf(#str)
- 计数
#define COUNT(num) num++
- 判断
#define IS_SUCCESS(ret) (SUCCESS == ret)
2.2 多行宏函数
多行宏函数书写可以用于进行一些判断后操作、FOR循环前置等一些包含多条语句的功能.
2.2.1 判断后进行操作
#define DEF_CHK_NUM_OPT(num, opt)\
do{\
if(100 == num)\
{\
PRINT(num);
opt++;\
}\
}while(0);
2.2.2 PRINT使用预定义宏
#define PRINT_STR(s)\
{\
printf("File:%s, Line%d:%s,Function:%s: %s\n", __FILE__,__LINE__,__FUNCTION__,s);
}
/* 各种预定义宏 */
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当程序严格遵循ANSI C标准时,该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。
2.2.3 PRINT 可变参数
参考snprintf函数类似
#define LOG(format, ...)\
{\
printf("%s, %d" format, __FUNCTION__, __LINE__, ##__VA_ARGS__);\
}
其实 `__VA_ARGS__` 就是一个可变参数的宏,替代上面的...
`##__VA_ARGS__` 就是当可变参数个数为0时,将参数列表中前面多余的,去掉.
如果不加##
, LOG("TEST")
会被替换成 printf("%s:%d" "TEST", func, LINE, );
,从替换结果看多了一个 ,
号。我们需要把这个 ,
去掉所以得使用 ##
去删除
3. 宏函数需要特别注意的地方
3.1 宏函数中的空格
#define f(x) ((x)-1)
#define f (x) ((x)-1)
上面第二种写法能够达到预期的效果吗?答案是不能。其表达的含义是f
代表 (x) ((x)-1)
.
但调用时f(3)
和f (3)
都能达到预期的效果.
3.2 宏需要分号吗
- 如果单纯是
#define num 5
,不要加分号,如果加了分号,做替换时会把分号也加上去; - 如果是宏函数,而这个函数在代码中需要分号,那就必须添加分号;
- 宏函数包含多条语句时,中间的语句该加的就得加;
3.3 宏后的 \ 字符
宏是以什么作为结束的,是以回车。所以宏函数包含多条语句时,后面的中间语句必须加 \
3.4 为什么使用do while形式
在写if else的宏函数时,如果不包裹起来,很容易产生悬挂else的问题,影响了之前的if else逻辑.
if(x == 0)
if( y== 0)
error();
else
{
z = x + y;
f(&z);
}
其实际含义为:
if(x == 0)
{
if( y== 0)
error();
else
{
z = x + y;
f(&z);
}
}
参考:
1、https://blog.csdn.net/sdoyuxuan/article/details/81948388
2、C陷阱和缺陷