在GCC的文档中建议使用如下的min宏定义:
引用:#define min(X,Y) \
(__extension__ \
({ \
typeof(X) __x=(X), __y=(Y); \
(__x<__y)?__x:__y; \
}) \
)
本文讨论了这样作法的意义。
[color=blue]1、传统的min带来的副作用[/color]
[color=blue]2、GCC中的({statement list})的扩展[/color]
[color=blue]3、typeof(expression)[/color]
[color=blue]4、__extension__的含义[/color][/u]
[color=blue]5、使用typeof和({})实现min,避免了副作用[/color]
[color=blue]附录1、旧版本的的GCC中的的解决方法[/color]
[color=blue]附录2、C++中使用template的解决方法[/color]
[color=blue]1、传统的min带来的副作用[/color]
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,原因在于[color=red]宏展开后x++被执行了两次[/color]:
引用:
int x = 1, y = 2;;
int main()
{
printf("min=%d\n", [color=red]x++ < y++ ? x++ : y++[/color]);
printf("x = %d, y = %d\n", x, y);
}
[color=blue]2、GCC中的({statement list})的扩展[/color]
({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
[color=blue]3、typeof(expression)[/color]
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;
}
[color=blue]4、__extension__的含义[/color]
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
$ 编译成功!
[color=blue]5、使用typeof和({})实现min,避免了副作用[/color]
#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",
[color=red]({
typeof(x) __x = (x++), __y = (y++); /* 定义了两个整形变量 */
(__x<__y)?__x:__y;
})[/color]
);
printf("x = %d, y = %d\n", x, y);
}
在执行min(x++, y++)期间,x++和y++只执行了一次,因而结果是正确的。
[color=blue]附录1、旧版本的的GCC中的的解决方法[/color]
旧版本的GCC提供了两个[color=red]内置[/color]的运算操作符:<?和>?, <?返回两个操作数中较小的一个,>?返回两个操作数中较大的一个,使用这两个操作符定义的min如下:
#define min(x, y) ((x) <? (y))
#define max(x, y) ((x) >? (y))
但是新版本的GCC文档中宣称:现在这两个运算操作符已经过时了,建议大家不要使用。
[color=blue]附录2、C++中使用template的解决方法[/color]
template <class type>
type min(type a, type b)
{
return a < b ? a : b;
}
mik 回复于:2007-05-12 16:15:56
顶~ 很好很清楚
白天晒太阳 回复于:2007-05-12 16:23:41
:P
missjiang 回复于:2007-05-12 17:15:06
像这样的写法:func(i++, i++)带来的问题,可以被看作是由i++、++i的副作用引起的。
使用传统的min导致上述程序出现问题的原因在于:x++被错误的执行了两次,这应该归结于宏的副作用,而与i++、++i的副作用无关。即使不使用i++、++i,宏min(a,b)执行时a、b之间的较小者被执行两次的副作用也是存在的,比如说:
int funcX()
{
printf("This is func X\n");
return 1;
}
int funcY()
{
printf("This is func Y\n");
return 2;
}
int main()
{
min(funcX(), funcY());
}
程序执行时,"This is func X"被打印了两次,显然不是我们期望的。
当然,我们可以保守的使用:
int main()
{
int x = funcX();
int y = funcY();
min(x, y);
}
显然,使用gcc文档中建议的min,可以使代码比较简洁。
emacsnw 回复于:2007-05-12 17:56:02
引用:原帖由 missjiang 于 2007-5-12 01:15 发表
像这样的写法:func(i++, i++)带来的问题,可以被看作是由i++、++i的副作用引起的。
使用传统的min导致上述程序出现问题的原因在于:x++被错误的执行了两次,这应该归结于宏的副作用,而与i++、++i的副作用 ...
这贴不错,顶一个。
为了可移植性,一般还要定义一个非GNUC版本的MIN/MAX,不知道怎样才能比较好的避免这个side effect?
mingyanguo 回复于:2007-05-12 20:53:33
能不用gcc的扩展还是不要用的好,至于说副作用,这是宏本身的原因,程序员应该意识到这个问题。
delimy 回复于:2007-05-13 13:53:01
还应该加上
(void)(&__x == &__y);
这样当类型不兼容时会有警告。尤其有符号和无符号的比较,这很重要。
choc 回复于:2007-05-13 14:09:44
支持!
[ 本帖最后由 choc 于 2007-5-13 14:12 编辑 ]
nully 回复于:2007-05-13 19:56:47
简单的问题复杂化,写成 inline的函数都比这个好。
langue 回复于:2007-05-13 20:45:18
.
我的个人信仰是,简单的事情简单解决,复杂的事情简化以后一样解决。
由此,inline 是较好的解决方案之一。
个人信仰,仅供参考。
.
飞灰橙 回复于:2007-05-13 20:52:07
引用:原帖由 missjiang 于 2007-5-12 15:57 发表
template <class type>
type min(type a, type b)
{
return a < b ? a : b;
}
c++中可没这么简单,这里的潜台词是a和b相应的构造函数存在,
如果不存在,这个模板就失败了。
所以己希望于在这里使用引用
template <class type>
type &min(type &a, type &b)
{
return a < b ? a : b;
}
但是,还是不行。。。。。。。
飞灰橙 回复于:2007-05-13 21:03:14
倒,是pp mm的发帖啊,早知道就不拍砖了
missjiang 回复于:2007-05-13 22:10:50
引用:原帖由 飞灰橙 于 2007-5-13 21:03 发表
倒,是pp mm的发帖啊,早知道就不拍砖了
我很喜欢这个抱着猫的小姑娘,但是我不是MM。

ccjjhua 回复于:2007-05-14 09:42:00
顶,大家都是高手啊,楼主能提出这么好的话题,参与讨论的又能举一反三。佩服。能看到此帖,真是有幸!
arenxl 回复于:2007-05-14 09:45:48
好贴,鉴定完毕
xiaomiao 回复于:2007-05-14 09:56:36
为什么不用C++? min()和max()都可以写成函数模板,泛型的思想很好啊
python.san 回复于:2007-05-22 20:38:16
不错不错,写的很好,开眼见了,我要好好收藏。
使用传统的min导致上述程序出现问题的原因在于:x++被错误的执行了两次,这应该归结于宏的副作用,而与i++、++i的副作用无关。即使不使用i++、++i,宏min(a,b)执行时a、b之间的较小者被执行两次的副作用也是存在的,比如说:
CODE:
[Copy to clipboard]
int funcX()
{
printf("This is func X\n");
return 1;
}
int funcY()
{
printf("This is func Y\n");
return 2;
}
int main()
{
min(funcX(), funcY());
}
程序执行时,"This is func X"被打印了两次,显然不是我们期望的。
这个例子举的很妙,:em02::em02::em02:
建议加精。
:em03:
rwen2012 回复于:2007-05-23 01:09:28
have a look at the macro at linux kernel:
/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
/*
* ..and if you can't take the strict
* types, you can specify one yourself.
*
* Or not use min/max at all, of course.
*/
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define max_t(type,x,y) \
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
xshhe 回复于:2007-06-04 10:27:34
搂主的帖子发的很不错,真的很收益,
能不用extension就不用这个建议很好,
一切简单化我是很同意的,所以针对这个问题的最佳解决办法是什么?
坚决不在min里面用表达式,并做到在min里用表达式是不合语法的意识,哈哈,这样就够时刻了!!
|