写的极详细,转载于此,原来我也一直在使用这种有缺陷的宏,真汗颜-_-!
在GCC的文档中建议使用如下的min宏定义:
#define min(X,Y) \
(__extension__ \
({ \
typeof(X) __x=(X), __y=(Y); \
(__x<__y)?__x:__y; \
}) \
)
本文讨论了这样作法的意义。
1、传统的min带来的副作用2、GCC中的({statement list})的扩展3、typeof(expression)4、__extension__的含义5、使用typeof和({})实现min,避免了副作用附录1、旧版本的的GCC中的的解决方法附录2、C++中使用template的解决方法1、传统的min带来的副作用min通常被定义成这样的宏:
- #define min(X,Y) ((X) < (Y) ? (X) : (Y))
复制代码 这种定义会带来一些副作用,看下面的例子:
- int x = 1, y = 2;
- int main()
- {
- printf("min=%d\n", min(x++, y++));
- printf("x = %d, y = %d\n", x, y);
- }
复制代码 执行完min(x++、y++),我们期望x的值为2,y的值为3。
但是,实际的结果是,执行完mini(x++、y++)后,x的值为3,y的值为3,原因在于
宏展开后x++被执行了两次:
int x = 1, y = 2;;
int main()
{
printf("min=%d\n", x++ < y++ ? x++ : y++);
printf("x = %d, y = %d\n", x, y);
}
2、GCC中的({statement list})的扩展({statement list})是一个表达式,逗号表达式类似,但是功能更强,({与})中可以包含有多条语句(可以是变量定义、复杂的控制语句),该表达式的值为statement list中最后一条语句的值,举例:
- int main()
- {
- int result = ({
- int i, sum = 0;
- for (i = 1; i <= 100; i++)
- sum+= i;
- sum;
- })
- printf("result=%d\n", result);
- }
- 运行结果:
- result=5050
复制代码 3、typeof(expression)typeof(expression)用来获取expression的类型,举例:
- int main()
- {
- int integer;
- typeof(100) i; /* 表达式100的类型是int,定义了int型变量i */
- typeof(integer) j; /* 表达式integer的类型是int,定义了int型变量j */
-
- i = 1;
- j = 2;
- }
复制代码 4、__extension__的含义GCC引入了很多标准C中的没有的扩展,如({和)},GCC提供了pednatic选项用于检测程序是否使用了GCC的扩展,当使用pedantic选项编译如下程序时
- int main()
- {
- int result = ({
- int i, sum = 0;
- for (i = 1; i <= 100; i++)
- sum+= i;
- sum;
- })
- printf("result=%d\n", result);
- }
复制代码 编译器发出警告:
- $ cc -pedantic test.c
- test.c: 在函数 ‘main’ 中:
- test.c:9: 警告:ISO C 不允许在表达式中使用花括号组
复制代码 编译器提醒程序员,这段C程序使用了不符合ISO C标准的语法,如果使用其他的编译器(非GCC)编译这段代码有能会出错。在所有使用GNU 扩展关键字的表达式之前加__extension__ 关键字后,使用pedantic选项编译时,编译器就不再发出警告信息:
- int main()
- {
- int result = __extension__({
- int i, sum = 0;
- for (i = 1; i <= 100; i++)
- sum+= i;
- sum;
- })
- printf("result=%d\n", result);
- }
- $ cc -pedantic test.c
- $ 编译成功!
复制代码 5、使用typeof和({})实现min,避免了副作用
- #define min(X,Y) \
- ({ \
- typeof(X) __x=(X), __y=(Y); \
- (__x<__y)?__x:__y; \
- })
复制代码 使用传统的min会出现问题的例子:
- int x = 1, y = 2;;
- int main()
- {
- printf("min=%d\n", min(x++, y++));
- printf("x = %d, y = %d\n", x, y);
- }
复制代码 它被扩展为
int x = 1, y = 2;;
int main()
{
printf("min=%d\n",
({
typeof(x) __x = (x++), __y = (y++); /* 定义了两个整形变量 */
(__x<__y)?__x:__y;
})
);
printf("x = %d, y = %d\n", x, y);
}
在执行min(x++, y++)期间,x++和y++只执行了一次,因而结果是正确的。
附录1、旧版本的的GCC中的的解决方法旧版本的GCC提供了两个
内置的运算操作符:?, ?返回两个操作数中较大的一个,使用这两个操作符定义的min如下:
- #define min(x, y) ((x) (y))
- #define max(x, y) ((x) >? (y))
复制代码 但是新版本的GCC文档中宣称:现在这两个运算操作符已经过时了,建议大家不要使用。
附录2、C++中使用template的解决方法template
type min(type a, type b)
{
return a < b ? a : b;
}
阅读(859) | 评论(0) | 转发(0) |