Chinaunix首页 | 论坛 | 博客
  • 博客访问: 681987
  • 博文数量: 111
  • 博客积分: 2109
  • 博客等级: 上尉
  • 技术积分: 1124
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-25 12:11
个人简介

通信码农,Emacs爱好者,业余IOS程序员,更业余的PM

文章分类

全部博文(111)

文章存档

2018年(2)

2016年(2)

2015年(2)

2014年(13)

2013年(21)

2012年(71)

分类:

2012-09-07 10:26:38

在网上看到一段出自chrome的求数组长度的源代码:
  1. template<typename T, size_t N>
  2. char (&ArraySizeHelper(T (&array)[N]))[N];
  3. #define arraysize(array) (sizeof(ArraySizeHelper(array)))
google为什么不使用我们一般求数组长度的方法:
  1. #define array_size(array) (sizeof(array) / sizeof(array[0]))
这是因为后者存在瑕疵:当我们传给array_size宏的不是数组名,而是指针的时候,宏array_size不会在编译的时候报错!它会得出错误的结果并且继续运行!而前者改进了后者存在的瑕疵,当我们将一个指针传给arraysize的时候,它会在编译期报错:
  1. #include <iostream>
  2. using namespace std;

  3. template<typename T, size_t N>
  4. char (&ArraySizeHelper(T (&array)[N]))[N];
  5. #define arraysize(array) (sizeof(ArraySizeHelper(array)))
  6. #define array_size(array) (sizeof(array) / sizeof(array[0]))

  7. int main()
  8. {
  9.     double a[200];
  10.     double *d = new double[10];

  11.     std::cout << array_size(a) << std::endl;
  12.     std::cout << array_size(d) << std::endl;   // 这里输出0,因为:(4 / 8) == 0

  13.     std::cout << arraysize(d) << std::endl;    // 这里报错
  14.     std::cout << arraysize(a) << std::endl;

  15.     return 0;
  16. }
上面的代码中:array_size(d)没有报错,他会输出错误的结果:0 !!!
而代码arraysize(d)会报错:error: no matching function for call to 'ArraySizeHelper(double*&)'
这就是两者的区别所在,也是前者的高明所在!!!
 
下面我们来研究一下chrome中的代码:
  1. template<typename T, size_t N>
  2. char (&ArraySizeHelper(T (&array)[N]))[N];
  3. #define arraysize(array) (sizeof(ArraySizeHelper(array)))
看到代码,进过仔细的思考,会有所理解,也会有一些疑问:
它主要的思想是将数组 T array[N]转化成数组 char ArraySizeHelper[M]. 在转化前后,数组所占的内存大小没有变,所以原数组的大小等于:sizeof(ArraySizeHelper).
这里的疑问是:
array本来就是数组名,也就是数组的首地址,为什么还要在他的前面再加上取地址符&呢?这里我们先看一小段代码:
  1. int main()
  2. {
  3.     double a[200];
  4.   
  5.     printf("a:%p\n&a:%p\n", a, &a);
  6.     return 0;
  7. }
运行这段代码,我们会发现:a和&a的地址是一样的,是相等的!!!
然后,我们将上面的代码改一下:
  1. int main()
  2. {
  3.     double a[200];

  4.     printf("a:%p\n&a:%p\n", a, &a);
  5.     printf("%d\n", (a == &a) );
  6.     return 0;
  7. }
编译,它报错:
error: comparison between distinct pointer types 'double*' and 'double (*)[200]' lacks a cast
从这段报错信息,我们可以得出结论:
虽然a和&a的地址值是一样的,是相等的,但是a和&a的类型却是不相同的a的类型是double *,而&a的类型是double (*)[200],a只是一个指针,而&a是一个指向数组的指针(如果可以区分开“指针数组”和“数组的指针”,那么就容易理解了。)这也就解释了:
  1. template<typename T, size_t N>
  2. char (&ArraySizeHelper(T (&array)[N]))[N];
这里array的前面为什么使用取地址符& ,因为这里需要的是double (*)[200],而不是double * .只有当我们传进去的是double (*)[200],模板的定义才是正确的。
所以当我们将 double *d = new double[10];  传给 arraysize(d) 时,才会在编译的时候报错!!!
另外:代码char (&ArraySizeHelper(T (&array)[N]))[N];中的两个N他们的值是不一样的:第一个N是double (*)[200]中的200 , 是传进去的值,而第二个N却是在将array传给char数组的构造函数:
 char (void *)[N]时计算出来的。
 
总结:
chrome中的ArraySizeHelper的定义利用了两点:
《1》利用了指针和数组的区别
《2》利用了char型数组的构造函数:char (void *)[N]来构造出一个新的数组ArraySizeHelper
 
阅读(1102) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~