本文主要对宏定义相关进阶的用法展开描述
常用宏定义用法
经常使用C编程的话都会一些基本的宏定义替换,例如变量、关键字、函数等,示例如下:
#define TOTAL_NUM 1000
#define GLOBAL_MOCK_VERIFY GlobalMockObject::verify();
#define CHECK_FUN_RET_INT(fun, val) do {int ret = fun; if (val == ret) {;}} while(0)
续行操作
我们经常是会遇到编写一个宏定义全部写在一行内太长,可读性也非常差,这个时候需要换行就可以用到我们的续行操作符了
#define RETURN_IF_RET_FAILURE(expr) \
{if(-1 == expr) {SYSLOG_NOTICE; return FAILURE;}}
#define MBQ_CAT_MEM(data, len) \
do { \
nwrite += len; \
if (nwrite > sz) { \
return -1; \
} else { \
memcpy(buf, data, len); \
buf += len; \
} \
} while (0)
#define 定义宏
#define 机制包括了一个规定,允许将参数替换文本中,这种实现通常被称为宏(macro)和定义宏(define macro)
#define SQUARE( x ) ((x) * (x))
也许会有小伙伴有疑问,为啥后面的x相乘要加这么多括号,看起来眼花缭乱的,去掉括号不是简单明了编写方便嘛?这就涉及到了定义宏的隐患问题啦,我们分内部隐患和外部隐患来解释一下
内部隐患
#define SQUARE( x ) x * x
int main (void) {
int val = 5;
printf("result is %d\n", SQUARE(val + 1));
return 1;
}
这里printf打印的值是36吗
显然不是,宏替换文本时,x 被替换成5 + 1,所以这段代码应该展开如下
printf("result is %d\n", 5 + 1 * 5 + 1);
打印输出应该为11
所以,参数内部加括号是必要的
外部隐患
内部隐患解决了,让我们再来看看外部隐患:
#define DOUBLE( x ) (x) + (x)
int main (void) {
int val = 5;
printf("result is %d\n", 10 * DOUBLE(val));
return 1;
}
这里printf打印的值是不是100呢?
显然也不是,让我们先将其展开
printf("result is %d\n", 10 * 5 + 5);
打印输出应该为55
所以,最外层加括号也是必要的
括号的使用
临时追加这一栏,因为今天遇到一件非常有意思的事,一位同事将他本地写好的代码给我拿去跑编译,其中有个宏定义编译一直在报错,起初我也是没看出来哪里有问题,大家第一眼能识别出来吗
#define GET_EADDR(func, addr) { (((func) & 0xFF) << 16) | ((addr) & 0XFFFF) }
乍一眼看是不是天衣无缝?该加的括号确实也加了,内部的语法也没有问题。
其实问题就出在这个宏定义是定义了一则运算,将func和addr两个入参通过位运算重构合并成一个数值,这个宏返回这个数值,它并不是单独一条语句,但是此处却用到了{},那么这段宏基本单位就是语句了,让我们看看用法就明白了
int result = GET_ADDR(0X10, 0X1001);
#的使用
使用 “#” 可以将宏的参数变为对应的字符串,示例如下:
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE " is "FORMAT "\n", VALUE);
int main (void) {
int i = 10;
PRINT("%d", i+3);
return 1;
}
此处打印应是the value of i + 3 is 13
## 的使用
## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符
#define ADD_TO_SUM(sum, num)\
sum##num += num;
ADD_TO_SUM(5, 10); //展开即是sum5 += 10;