Chinaunix首页 | 论坛 | 博客
  • 博客访问: 335295
  • 博文数量: 79
  • 博客积分: 2466
  • 博客等级: 大尉
  • 技术积分: 880
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-07 16:47
文章分类

全部博文(79)

文章存档

2014年(3)

2012年(7)

2011年(14)

2010年(2)

2009年(2)

2008年(2)

2007年(18)

2006年(31)

分类: C/C++

2011-07-12 11:32:49

比较为程序员熟知的做法是

#define arrsz(a) (sizeof(a)/sizeof(a[0]))

 

检查下面的代码:

std::vector v(23);

printf("sizeof v is %d\n", arrsz(v));

 

编译和运行都没有报错,但得到了一个错误的结果——3

因为sizeof(v)12sizeof(int)4。这里sizeof(v)并不是数组全部元素的总的尺寸。

 

在《Unix编程艺术》中,Eric Raymond提出Unix社区中得到广泛认可的一条原则:

Principle of Most Surprise: When you must fail, fail noisily and as soon as possible.

当代码应当失败时,要尽早而且尽可能惹人注意地失败。

而不是像上面我们遇到的例子那样,不声不响地失败,同时隐藏了一个严重的错误。

 

问题有了。解决问题的第一个尝试:

#define arrsz2(a) (sizeof(a)/sizeof(0[a]))

 

0[a]符合C++的语法规范。根据在C++标准(ISO/IEC 14882:2003(E))的8.3.4小节的第6点,原文是:

Except where it has been declared for a class (13.5.5), the subscript operator [] is interpreted in such a way that E1[E2] is identical to *((E1)+(E2)).  Because of the conversion rules that apply to +, if E1 is anarray and E2 an integer, then E1[E2] refers to the E2-th member of E1.  Therefore, despite its asymmetric appearance, subscripting is a commutative operation.

 

对数组来说,e1[e2]e2[e1]是等价的,普通数组的下标运算符满足交换律。但是下面的代码编译时就会出错:

std::vector v(23);

printf("sizeof v is %d\n", arrsz2(v));

因为vector模板提供了operator[]v[0]没问题。但0[v]被解释为0.operator[](v),这个操作符是未定义的;*(0+v)当然也不是合法的表达式。目标达到了……吗?

 

long longArr[11];

long *p = longArr;

printf("sizeof v is %d\n", arrsz2(p));

 

静默失败的问题再次出现。结果是1。现在的要求是,求数组长度的这个宏,不能默默地接受vector或指针类型后默默输出一个错误的结果。Google的牛人们在Chrome的源码里这样解决的:

 

template

char (&ArraySizeHelper(T (&array)[N]))[N];

 

#define arraysize(array) (sizeof(ArraySizeHelper(array)))

 

这模板是个函数模板。

函数参数是一个引用,这个引用指向一个数组,长度为N,数组元素类型为T

函数返回一个引用,这个引用也指向一个数组,长度为N,元素类型为char

对这个长度为Nchar数组使用sizeof即得到N

 

最后的这个宏,如果把指针或vector类型传入,都会引起编译错误。另外注意如果写成

T &array[N]

就不是一个“数组的引用”了,而是一个元素类型为引用的数组(这在C++中是非法的)。

实际上这个做法背后还有一个不小的话题,就是数组形参,《C++必知必会》中有专题讲解。

 

这个话题的原始材料来自:

阅读(945) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~