在网上看到一段出自chrome的求数组长度的源代码:
-
template<typename T, size_t N>
-
char (&ArraySizeHelper(T (&array)[N]))[N];
-
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
google为什么不使用我们一般求
数组长度的方法:
-
#define array_size(array) (sizeof(array) / sizeof(array[0]))
这是因为后者存在瑕疵:当我们传给array_size宏的不是数组名,而是指针的时候,宏array_size不会在编译的时候报错!它会得出错误的结果并且继续运行!而前者改进了后者存在的瑕疵,当我们将一个指针传给arraysize的时候,它会在编译期报错:
-
#include <iostream>
-
using namespace std;
-
-
template<typename T, size_t N>
-
char (&ArraySizeHelper(T (&array)[N]))[N];
-
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
#define array_size(array) (sizeof(array) / sizeof(array[0]))
-
-
int main()
-
{
-
double a[200];
-
double *d = new double[10];
-
-
std::cout << array_size(a) << std::endl;
-
std::cout << array_size(d) << std::endl; // 这里输出0,因为:(4 / 8) == 0
-
-
std::cout << arraysize(d) << std::endl; // 这里报错
-
std::cout << arraysize(a) << std::endl;
-
-
return 0;
-
}
上面的代码中:array_size(d)没有报错,他会输出错误的结果:0 !!!
而代码arraysize(d)会报错:error: no matching function for call to 'ArraySizeHelper(double*&)'
这就是两者的区别所在,也是前者的高明所在!!!
下面我们来研究一下chrome中的代码:
-
template<typename T, size_t N>
-
char (&ArraySizeHelper(T (&array)[N]))[N];
-
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
看到代码,进过仔细的思考,会有所理解,也会有一些疑问:
它主要的思想是将数组 T array[N]转化成数组 char ArraySizeHelper[M]. 在转化前后,数组所占的内存大小没有变,所以原数组的大小等于:sizeof(ArraySizeHelper).
这里的疑问是:
array本来就是数组名,也就是数组的首地址,为什么还要在他的前面再加上取地址符&呢?这里我们先看一小段代码:
-
int main()
-
{
-
double a[200];
-
-
printf("a:%p\n&a:%p\n", a, &a);
-
return 0;
-
}
运行这段代码,我们会发现:a和&a的地址是一样的,是相等的!!!
然后,我们将上面的代码改一下:
-
int main()
-
{
-
double a[200];
-
-
printf("a:%p\n&a:%p\n", a, &a);
-
printf("%d\n", (a == &a) );
-
return 0;
-
}
编译,它报错:
error: comparison between distinct pointer types 'double*' and 'double (*)[200]' lacks a cast
从这段报错信息,我们可以得出结论:
虽然a和&a的地址值是一样的,是相等的,但是a和&a的类型却是不相同的。a的类型是double *,而&a的类型是double (*)[200],a只是一个指针,而&a是一个指向数组的指针(如果可以区分开“指针数组”和“数组的指针”,那么就容易理解了。)这也就解释了:
-
template<typename T, size_t N>
-
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。
阅读(439) | 评论(0) | 转发(0) |