ARRAYSIZE宏
不同的C++实现有很多的库中都有一个计算数组长度的宏,名字大致都叫ARRAYSIZE,如VS中就定义了这个宏,它的使用很简单,len = ARRAYSIZE(a),只要传入的a是一个数组就能计算出这个数组的长度。实际上计算数组的常用套路是这样的
#define ARR_SIZE(A) (sizeof(A)/sizeof(A[0]))
上面这个计算方法有很多问题,如果传入的A是指针那就不可能计算出来正确的数组长度了,而现实中数组退化成指针的情况非常多。所以综合考虑下结合C++的特性就出现了ARRAYSIZE这个新的C++下使用的全新宏。它克服了上面说的传统arraysize的问题,那么直接看一下它的实现:
template<typename T, unsigned int N>
char (&array_size_fake_func(T(&)[N]))[N];
#define MYARRAYSIZE(A) sizeof(array_size_fake_func(A))
上面的语法极难看懂,因为平时几乎用不到,那么我们来分析一下这段简单的代码。它的核心实际上是上面的模板函数的一个声明,抛开模板,这个函数的参数和返回值都是T类型数组(数组长度N)的引用,很不常见吧,这里容易忽视的一个语法是c/c++是不支持函数返回数组的(java可以),所以这里返回数组的引用(或指针),而数组的引用语法本身很少用,并且写法比较怪。弄清楚了函数的结构,我们先看一下数组引用的使用
//返回一个数组引用的函数,参数也是一个数组引用
static int (&ret_arr_ref(int(&ref)[6]))[6]
{
return ref;
}
int a[6] = {0};
int (&arf)[6] = a;
arf[0] = 1;
int (&arf2)[6] = ret_arr_ref(a);
//arf 和 arf2 均是指向数组a的数组引用
以上代码比较清晰的解释了数组引用如何用,那么回到刚才的模板函数,通过传入参数的推导(如果是一个数组类型变量),编译器就可以推导出T和N,这里我们更关心N的值,我们再通过拿到返回值char[N]的引用ref,求它的长度len = sizeof(ref),显然len就是N,通过上面的推导,就可以把N的值传递出来了。这里的好处是,如果传递给函数的参数不是数组,而是退化的指针,那么编译会报错,因为无法推导出N,这就有效的防止了数组退化成指针的问题,从编译层面彻底杜绝了这种情况的发生,从而保证计算数组长度的准确性。
在看一下宏#define MYARRAYSIZE(A) sizeof(array_size_fake_func(A)),其中sizeof(array_size_fake_func(A)),sizeof是编译期行为,所以这里函数并不产生调用,只是产生函数返回值类型,这个函数甚至不需要调用就能推导出N,所以这个宏没有额外的运行期损耗,所以这就是它的优势,没有类型不明确造成的错误(数组退化指针),没有运行期效率损失。
这个神奇的宏实现背后隐藏着很多故事,通过层层分析我们大致了解了它背后的实现原理和优势,以后碰到计算数组长度的场景尽量用这个宏吧,安全又快捷。
网友评论