一直在嵌入式中混日子,很多知识点被遗忘的差不多了都,最近也是项目setting需要用宏定义来实现不同setting的区分,在makefile里倒是加好了宏,但是逻辑运算到着实有点手生就搜了搜资料,这里也给自己记录下宏的知识点吧。
不过呢先说一下我对宏定义的理解,宏定义就想象成为本替换,想了解清楚就文本替换还原回去就能明白了。
下面我们就开始进入主题,来进一步了解一下宏:

1. 宏简单实用演示:

1.1宏的简单使用说明

#define square(x) x*x
/*
*下面我们就来用一下这个宏
*/
printf("result = %dn",square(5));
/*无可厚非,结果就是5*5=25*/
int a = 5;
printf("result1 = %dn",square(a+1));
/*第一眼可能会脱口而出36,
*但是你文本替换一下就是这个样子x==a+1,所以x*x=a+1 * a+1=11
*/
//上面的可能不是你的本意,你的本意是要36这个值,就要用下面这个种方式
#undef square
#define square(x) (x)*(x)
printf("result1 = %dn",square(a+1));
/*
*这样子我们重新替换一下就是x==a+1,(x)*(x)=(a+1)*(a+1)
*这也算是宏定义的一个误区,需要慎重,不确定的时候建议使用括号包起来
*/

1.2宏定义在我们code中颇受欢迎,会用了有很多便处也有很多技巧,比如宏定义可以用来禁止一些系统function的使用,如下:

#define x_malloc(type,size) ((type*)malloc(size))
#define malloc 

这样就无法直接在该函数里调用这个系统api了, 但是我们可以通过自己的新定义的万能api x_malloc来代替系统malloc函数,这样用起来就方便多了。
1.3其次呢 宏定义也是可以接收你的function parameter的,如下:

#define  Printf(argc,argv) printf("%s,%d",argc,argv)int main(char* argc[], int argv){Printf(argc,argv);return 0;}

1.4宏还可以代替结构体来定义一个结构体变量:

#ifdef MEMBER
#undef MEMBER
#endif
#define MEMBER(type,member) (type member;)
#define STRUCT 
MEMBER(int, u4Size)
MEMBER(char, u1Ch)
MEMBER(bool, bFlag)

int main(void)
{STRUCT A;A.u4Size=10;A.u1Ch='a';A.bFlag=true;return 0;
}

2. 宏定义的条件展示:

/*
*同样宏定义也是有逻辑操作的,比如我们判断是否有定义过这个宏可以这样子:
*/#if define symbol//#ifdef symbol#endif#if !define symbol//ifndef symbol#endif/** 有些时候我们迫不得已要用到下面这种逻辑来隔开不同的code setting*/#ifdef Option1//implement code for option1#elif (define option2)//implement code for option2#else//implement code for default setting#endif/**还有下面这种同时判断两个宏定义的**/#if (define Option1)&&(define option2)//implement common code for both of option1 and option2#elif Option1//implement code only for option1#elif (define option2)//implement code only for option2#else//implement code for default setting#endif

3. 宏和函数

在代码中我们经常会用到宏来定义一个简单的函数来供我们使用,但是这两个有什么区别呢?
我们来简单列一下宏和函数区别:
3.1 宏定义的代码较为简短且都是在预处理的时候就会插入到整个代码中,他的生存期和程序一样;函数适合代码较长的使用,每次调用会需要重新load同一个地方的代码
3.2宏因为一直在程序中所以执行速度快,函数每次执行需要重新load相比比较慢
3.3宏的参数不care类型,甚至可以吧类型当做宏的参数传递,但是函数就不行了(还记得最前面我们举例的时候有展示一个malloc的替换嘛,哪里我们可以直接把类型当做宏的参数来使用)
3.4宏对操作符优先级如果不加括号结果不可预期,但是函数是可以预期
3.5结果返回,宏的结果返回也有可能不预期,如下面我们的求最大值,但是函数的返回不会造成那种特殊的效果

宏定义还有一个副作用就是它有“持续效果”,这个就是它可以再原来的base上持续进行会改变你的初衷,具体的我们举个例子来看下:

int x=5, y=6;
int u4MAx(int a, int b)
{return (a>b:a,b);
}
#define Max(a,b) ((a)>(b)?(a):(b))
/*
*我们不管调用多少次u4Max(x++,y++),x和y的增长都是一样的,第一次调用后x=6,y=7,u4Max(x++,y++)=7;
*但是Max(x++,y++),就不一样了,第一次调用后x=6,y=8,Max(x++,y++)=7
*我们把上面的具体展开看下Max(x++,y++)=((x++)>(y++)?(x++):(y++))
*这样就很明显x和y中有一个被多加了一次,这也是我们不预期的一个操作,所以这个持续效果值得我们日常撸码时候注意
*/

宏我们就简单记录这么多,使用宏一定要注意item3里面的细节,不然很容易造成很多不预期得结果导致不必要的麻烦。