前言
之前笔试过程中,我自认为使用宏来求得最值这方面已经了解的足够清楚。我一直是使用下面两个宏来做题的。直到我最近闲暇下来,阅读了《嵌入式C语言自我修养》这本书,才发现自己很多情形没有考虑到位。
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
一、同类型的变量,如果出现对参数自增自减操作,该如何修改
如果按照前言的版本,显然在下面的情况是无法正常运行的
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{int i = 2;int j = 6;printf("max=%d",MAX(i++,j++)); //此处运行后i=4,j=7;且max=7;return 0;
}
该测试代码期待的运行结果是,i、j两个变量进行对比,max存储i、j未自增前的最大值,而经过MAX宏运算后,i、j各自自增1,即期待的运行后结果为max = 6, i = 3,j=7。
与期待的结果不同,实际运行的结果为max = 7, i =4 ,j=7。
为什么会出现这个状况呢,显然是进行宏替换时,将自增运算符也替换了,如下面代码所示。
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{int i = 2;int j = 6;//printf("max=%d",MAX(i++,j++));printf("max=%d",((i++) > (j++) ? (i++) : (j++))); //宏替换后的代码return 0;
}
显然我们无法要求宏的使用者都不使用自增运算符,那么如何解决这个bug呢?解决这个问题的关键是让三元运算符表达式部分不被替换成自增自减运算符即可。这里使用新建一个表达式来解决。
#define MAX(x,y) ({ \int _x = (x); \int _y = (y); \_x > _y ? _x : _y; \
})int main(void)
{int i = 2;int j = 6;printf("max=%d",MAX(i++,j++));return 0;
}
在宏定义中,新建表达式将x、y存起来,在使用新建的变量_x、_y来进行比较即可解决这个bug。新建表达式后,_x\_y存储的是未自增前的x\y表达式的值;而x\y表达式被赋值给_x\_y后,x\y表达式其中如果有自增自减运算符则会运行,而后的比较部分没有调用到x\y表达式,即x\y表达式不会宏替换导致多次自增自减。
至此,同类型变量下, 参数包含自增自减运算符导致多次自增自减的bug消除了。那么能否泛化一下,使其宏定义中可以指定其他类型的变量呢?
二、修改宏对比中参数的类型
上一节结尾提到,宏定义中创建的_x\_y变量均为int类型,难不成float、char等类型要对比都需要重复写对比宏吗?这一小节来将对比宏进行泛化,不指定对应的类型。
最简单的解决思路是,宏中新增一个变量,传入要对比参数的类型,这样就很简单解决了。
#define MAX(type,x,y) ({ \type _x = (x); \type _y = (y); \_x > _y ? _x : _y; \
})int main(void)
{int i = 2;int j = 6;printf("max=%d\n",MAX(int,i++,j++));printf("max=%f\n",MAX(float,3.14,3.15));return 0;
}
通过传入宏参数的类型显然很方便,但是就是要偷懒,就是不想手动传入类型该如何做呢?
三、推断变量类型
C语言里面有typeof可以来获取变量的类型,这里同样可以使用。
#define MAX(x,y) ({ \typeof(x) _x = (x); \typeof(y) _y = (y); \_x > _y ? _x : _y; \
})int main(void)
{int i = 2;int j = 6;printf("max=%d\n",MAX(i++,j++));printf("max=%f\n",MAX(3.14,3.15));return 0;
}
但显然这里存在一个问题,如果x,y变量类型不一致怎么办,是否能够智能提醒变量类型不一致呢?
四、变量类型匹配提醒
#define MAX(x,y) ({ \typeof(x) _x = (x); \typeof(y) _y = (y); \(void) (&_x == &_y); \_x > _y ? _x : _y; \
})
在宏定义中,我们添加了(void)(&_x == &_y)进行类型的对比,那么这里是如何起作用的呢?
c语言编译器当检测到不同类型的指针进行比较时,编译器会发出一个警告,这样就可以提示到用户:你当前用两个不同类型的变量运行MAX\MIN宏。而前面添加(void)的原因是,当两个变量是同样类型时,比较过程会提示一个warning,加上(void)后,可以消除该警告。
到此,最大最小宏的编程之旅到此结束,希望阅读到这里的读者有所收获。