工欲善其事必先利其器-C语言拓展–嵌入式C语言(二)
表达式、语句和代码块–>宏
前言
提起宏,脑子里第一时间想到的就是
#define PI 3.14
但是最近在看Linux内核代码的时候,我简直了,各种花里胡哨的宏定义,我只能说有点东西。
这里的宏加入了语句表达式,一下子宏就变得精彩了。
举个栗子–>请定义一个宏,求两个数的最大值。
合格
#define MAX(x,y) x > y ? x : y
那么我们来测试一下?
MAX(1,2) MAX(2,1) MAX(2,2) MAX(1!=1,1!=2)
如,测试第4行语句,当宏的参数是一个表达式,发现实际运行结果为max=0,和我们预期结果max=1不一样。
这是因为,宏展开后,变成如下样子。
printf("max=%d", 1!=1 > 1!=2 ? 1!=1: 1!=2);
因为**比较运算符>的优先级为6,大于!=(优先级为7),**所以在展开的表达式中,运算顺序发生了改变,结果就和预期不一样了。
为了避免这种展开错误,我们可以给宏的参数加一个小括号(),防止展开后表达式的运算顺序发生变化。
#define MAX(x,y) (x) > (y) ? (x) : (y)
中等
是不是觉得上面的就不错了?
那么再来个测试
3+MAX(1+2)
这按说应该等于5,但是实际上等于1。经过上面你肯定猜到了是运算符优先级变化的问题,我们展开宏的时候。
因为运算符+的优先级大于比较运算符>,所以这个表达式就变为4>2?1:2。
于是这个时候我们继续修改,用小括号将宏定义包起来。这样避免当一个表达式同时含有宏定义和其他高优先级运算符时,破坏整个表达式的运算顺序。。
#define MAX(x,y) ((x)>(y) ? (x):(y))
到这里我们加了两次括号了
良好
是不是感觉自己又行了,别高兴太早哦。
整个栗子试试
i=2; j=6; MAX(i++,j++)
展开
((2++)>(6++) ? (2++):(6++))
结果–>7,有没有选6的小伙伴
这是因为变量i和j在宏展开后,做了两次自增运算,导致打印出的i值为7。
在C语言编程规范中,使用宏时一般是不允许参数变化的。
但是总有些调皮鬼,要这么写应该怎么办呢?
–>语句表达式
我们可以使用语句表达式来定义这个宏,在语句表达式中定义两个临时变量,分别来暂时存储i和j的值,然后使用临时变量进行比较,这样就避免了两次自增、自减问题。
看这个有点绕,看看代码
#define MAX(x, y)({ \ int _x = x; \ int _y = y; \ _x > _y ? _x : _y;\ })
定义了2个局部变量_x、_y来存储宏参数x和y的值,然后使用_x和_y来比较大小,这样就避免了i和j带来的2次自增运算问题。
将参数与符号分开了
优秀
上面这个级别其实已经很nice了,再内核代码中我也看到了一些这样的,原来是这样的。
不过我们需要再进一步的提高自己的要求。
上面的宏只能对整形的数据进行操作,但是当不是整形难道要重新的定义吗?
那可不行哦,来来来看看下面。
#define MAX(type, x, y)({\ type _x = x; \ type _y = y; \ _x > _y ? _x : _y; \ }) MAX(int, i++, j++); MAX(float, 3.14, 3.15);
在这里我们通过一个type这不就解决了,其实宏这种东西就是字符串,我觉得就是字符串的拼接替代,这样是不是会有帮助理解一点,如果没有,当我没说。
牛啊牛啊
但是我们这个type是不是感觉有点太突兀了,每次还的专门整个变量类型,如果我不知道呢?有没有想到能自动获取类型的。
这里有办法了—想起这个没–>typeof
#define max(x, y)({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y);\ _x > _y ? _x : _y ; })
有点疑惑为什么有个 (void) (&_x == &_y);\ 这个玩意?
这是用来给用户警告的,对于不同类型的指针进行比较,编译器会发出警告,提示你。
warning:comparison of distinct pointer types lacks a cast
其次这个数的比较,但是这个比较的结果用不上,可能会报warning,这个时候加一个(void)后,这个警告就pass了。(因为这个宏是发挥了作用的哦,宏在这里进行了比较呢)
内核中我看到了大量的这样的语句表达式,刚刚开始看我都问自己这还是我曾经见过的宏定义吗?
在Linux内核中,max_t和min_t的宏定义,就使用了语句表达式。
这下你知道了撒,其实就是我们常见宏的升级版本。
[学习资料]:《嵌入式C语言自我修养:从芯片、编译器到操作系统》