免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10411 | 回复: 18
打印 上一主题 下一主题

对min(a,b)这个宏的讨论 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-05-12 15:57 |只看该作者 |正序浏览
在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通常被定义成这样的宏:
  1. #define min(X,Y) ((X) < (Y) ? (X) : (Y))
复制代码

这种定义会带来一些副作用,看下面的例子:

  1. int x = 1, y = 2;
  2. int main()
  3. {
  4.    printf("min=%d\n", min(x++, y++));
  5.    printf("x = %d, y = %d\n", x, y);
  6. }
复制代码

执行完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中最后一条语句的值,举例:

  1. int main()
  2. {
  3.     int result = ({
  4.      int i, sum = 0;
  5.      for (i = 1; i <= 100; i++)
  6.           sum+= i;
  7.      sum;
  8.     })
  9.     printf("result=%d\n", result);
  10. }
  11. 运行结果:
  12. result=5050
复制代码


3、typeof(expression)
typeof(expression)用来获取expression的类型,举例:

  1. int main()
  2. {
  3.    int integer;
  4.    typeof(100) i;  /* 表达式100的类型是int,定义了int型变量i */
  5.    typeof(integer) j; /* 表达式integer的类型是int,定义了int型变量j */
  6.    
  7.    i = 1;
  8.    j = 2;
  9. }
复制代码


4、__extension__的含义
GCC引入了很多标准C中的没有的扩展,如({和)},GCC提供了pednatic选项用于检测程序是否使用了GCC的扩展,当使用pedantic选项编译如下程序时

  1. int main()
  2. {
  3.     int result = ({
  4.      int i, sum = 0;
  5.      for (i = 1; i <= 100; i++)
  6.           sum+= i;
  7.      sum;
  8.     })
  9.     printf("result=%d\n", result);
  10. }
复制代码

编译器发出警告:

  1. $ cc -pedantic test.c
  2. test.c: 在函数 ‘main’ 中:
  3. test.c:9: 警告:ISO C 不允许在表达式中使用花括号组
复制代码

编译器提醒程序员,这段C程序使用了不符合ISO C标准的语法,如果使用其他的编译器(非GCC)编译这段代码有能会出错。在所有使用GNU 扩展关键字的表达式之前加__extension__ 关键字后,使用pedantic选项编译时,编译器就不再发出警告信息:

  1. int main()
  2. {
  3.     int result = __extension__({
  4.     int i, sum = 0;
  5.      for (i = 1; i <= 100; i++)
  6.           sum+= i;
  7.      sum;
  8.     })
  9.     printf("result=%d\n", result);
  10. }
  11. $ cc -pedantic test.c
  12. $ 编译成功!
复制代码


5、使用typeof和({})实现min,避免了副作用

  1. #define min(X,Y) \
  2. ({  \
  3. typeof(X) __x=(X), __y=(Y);  \
  4. (__x<__y)?__x:__y;  \
  5. })
复制代码

使用传统的min会出现问题的例子:

  1. int x = 1, y = 2;;
  2. int main()
  3. {
  4.    printf("min=%d\n", min(x++, y++));
  5.    printf("x = %d, y = %d\n", x, y);
  6. }
复制代码

它被扩展为
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如下:

  1. #define min(x, y) ((x) <? (y))
  2. #define max(x, y) ((x) >? (y))
复制代码

但是新版本的GCC文档中宣称:现在这两个运算操作符已经过时了,建议大家不要使用。

附录2、C++中使用template的解决方法
template <class type>
type min(type a, type b)
{
     return a < b ? a : b;
}

论坛徽章:
0
19 [报告]
发表于 2007-06-04 10:27 |只看该作者
搂主的帖子发的很不错,真的很收益,
能不用extension就不用这个建议很好,
一切简单化我是很同意的,所以针对这个问题的最佳解决办法是什么?
坚决不在min里面用表达式,并做到在min里用表达式是不合语法的意识,哈哈,这样就够时刻了!!

论坛徽章:
0
18 [报告]
发表于 2007-05-23 01:09 |只看该作者
have a look at the macro at linux kernel:



  1. /*
  2. * min()/max() macros that also do
  3. * strict type-checking.. See the
  4. * "unnecessary" pointer comparison.
  5. */
  6. #define min(x,y) ({ \
  7.         typeof(x) _x = (x);        \
  8.         typeof(y) _y = (y);        \
  9.         (void) (&_x == &_y);                \
  10.         _x < _y ? _x : _y; })

  11. #define max(x,y) ({ \
  12.         typeof(x) _x = (x);        \
  13.         typeof(y) _y = (y);        \
  14.         (void) (&_x == &_y);                \
  15.         _x > _y ? _x : _y; })

  16. /*
  17. * ..and if you can't take the strict
  18. * types, you can specify one yourself.
  19. *
  20. * Or not use min/max at all, of course.
  21. */
  22. #define min_t(type,x,y) \
  23.         ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
  24. #define max_t(type,x,y) \
  25.         ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
复制代码

论坛徽章:
0
17 [报告]
发表于 2007-05-22 20:38 |只看该作者
不错不错,写的很好,开眼见了,我要好好收藏。
使用传统的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"被打印了两次,显然不是我们期望的。

这个例子举的很妙,
建议加精。

论坛徽章:
0
16 [报告]
发表于 2007-05-14 09:56 |只看该作者
为什么不用C++? min()和max()都可以写成函数模板,泛型的思想很好啊

论坛徽章:
0
15 [报告]
发表于 2007-05-14 09:45 |只看该作者
好贴,鉴定完毕

论坛徽章:
0
14 [报告]
发表于 2007-05-14 09:42 |只看该作者
顶,大家都是高手啊,楼主能提出这么好的话题,参与讨论的又能举一反三。佩服。能看到此帖,真是有幸!

论坛徽章:
0
13 [报告]
发表于 2007-05-13 22:10 |只看该作者
原帖由 飞灰橙 于 2007-5-13 21:03 发表
倒,是pp mm的发帖啊,早知道就不拍砖了

我很喜欢这个抱着猫的小姑娘,但是我不是MM。

论坛徽章:
0
12 [报告]
发表于 2007-05-13 21:03 |只看该作者
倒,是pp mm的发帖啊,早知道就不拍砖了

论坛徽章:
0
11 [报告]
发表于 2007-05-13 20:52 |只看该作者
原帖由 missjiang 于 2007-5-12 15:57 发表
template <class type>
type min(type a, type b)
{
     return a < b ? a : b;
}


c++中可没这么简单,这里的潜台词是a和b相应的构造函数存在,
如果不存在,这个模板就失败了。

所以己希望于在这里使用引用

  1. template <class type>
  2. type &min(type &a, type &b)
  3. {
  4.      return a < b ? a : b;
  5. }
复制代码


但是,还是不行。。。。。。。
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP