C++中获得数组的大小—CountOf实现

在C++中怎样获得数组的大小?

 

版权:the1(博客名) (博客地址)http://blogs.msdn.com/b/the1/

翻译:magictong(童磊) 2011年4月

原文地址:http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx

 

         P.S. 之前在一篇文章(http://blog.csdn.net/magictong/archive/2011/04/14/6322084.aspx)中说过VS2005下面_countof的实现,最后提到过为什么使用模板而抛弃之前的实现,后来在网上看到了这篇文章,感觉讲得比较清楚,就翻译了一下,还算简单清楚。

 

 

         问题非常简单,:给定一个数组(例如:int x[10]就是定义了一个名为x的整型数组),你怎样获得这个数组里面元素的个数?

         最简单最明显的方法是定义如下的一个宏(定义 1):

                   #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))

 

         我们不能说这种定义是不正确的,因为当你传一个数组给这个宏的时候,它确实能给你返回正确的结果。然而,当你把一个不是数组的的变量传递给上面这个宏的时候,它得出了一个错误的结果,譬如,如果你有这样一个指针声明:

                   int* p;

 

         那么在那种整型指针和整型变量具有相同的大小的机器上_countof(p)计算出来的结果将总是1(例如:win32平台就是这样)。

         而且,这个宏还会错误的接受那种重载了方括号运算符(operator[])的类的对象作为参数。举个例子,假如你写了这样一个类:

                   class IntArray

{

private:

         int * p;

         size_t size;

 

public:

         int & operator [] ( size_t i );

x;

 

         那么sizeof(x)的值将是x对象的大小,而不是x.p这个指针指向的缓冲区的大小。因此,通过_countof(x)你无法得到正确的结果。

         从上面的讨论,我们可以得出结论定义 1并不是太好,因为编译器无法阻止你错误的使用它。它也能保证只能传递一个数组给它。

         我们该怎么办?还有更好的选择吗?

         答案是肯定的。

如果我们希望编译器来验证传递给_countof的参数始终是一个数组,那么我们必须首先找到只允许传递一个数组参数这样一个语境。在这种语境下任何非数组的表达式都会予以拒绝(译注:编译失败)。

         初学者可能会试着这样尝试来定义(定义 2):

template <typename Tsize_t N>

size_t countofT array[N] )

{

         return N;

}

        

他们希望这个模版函数接受一个有N个元素的数组作为参数,并且返回N。

然而不幸的是,这种写法无法通过编译,因为在C++里面把一个数组作为参数跟把一个指针作为参数是等同的,也就是说上面的定义等价于:

template <typename Tsize_t N>

size_t countofT * array )

{

         return N;

}

 

现在就很明显了,函数体根本无法知道N到底是个啥。

但是,如果一个函数需要接受一个数组的引用来作为参数,那么编译器就将确保实际数组参数的大小和声明的是匹配的。这意味着我们只需要把定义 2稍微修改一下就可以了(定义 3):

template <typename Tsize_t N>

size_t countofT (&array)[N] )

{

         return N;

}

 

现在_countof将会很正常的工作,而且会阻止你错误的给它传递一个指针。但是,现在它是一个函数,而不是一个宏了。这意味着你不能在编译时就计算出它的大小。特别是,你现在不能写下这样的代码了:

int x[10];

int y[ 2*countof(x) ]; // twice as big as x

 

现在该怎么办?

有人(我不知道他是谁 - 我是从一个不出名的作者那看到的这样一个代码片段)想出了一个聪明的点子:把N从函数体移到返回类型里面去(例如:使函数返回一个拥有N个元素的数组),那么我们可以得到这个N的值,甚至不需要去实际调用这个函数。

准确地说,我们必须使这个函数返回一个数组引用,因为C++不允许返回一个具体的数组。具体写法如下:

template <typename Tsize_t N>

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

#define _countof(array) (sizeof(_ArraySizeHelper(array)))

 

不可否认的是这个语法看起来实在的是有点恶心而可怕了(译注:我第一次看到的时候也懵了),因此对它进行一些解释是必要的。

首先,我们看一下最外层的东西:

         char ( &_ArraySizeHelper( ... ))[N];

 

这个表达式应该这样理解:_ArraySizeHelper是一个函数,并且它的返回值是一个具有N个元素的char数组的引用(注意&符号)。

接下来,我们看这个函数的参数:

         T (&array)[N]

 

它是一个具有N个元素的的T类型数组的引用。

最后,_countof被定义为计算函数_ArraySizeHelper返回值结果的大小。请注意,我们甚至不需要去实现_ArraySizeHelper()——一个声明就足够了。

使用新的定义,我们又可以写这样的代码了,现在它的结果也符合我们的预期了:

int x[10];

int y[ 2*countof(x) ]; // twice as big as x

 

现在可以高兴了么?呃,我想这个定义应该比我们已经见过的一些定义要好,但是它仍然不是我想要的。主要是,它无法和一个定义在函数里面的类型很好的工作。这是因为模版函数_ArraySizeHelper期望的类型是全局范围都可以访问的。(译注:这个地方有点奇怪,函数中定义的类型本身就是只有那个函数中可见的,在那个定义类型的函数中应该是可以使用这个宏的,但是函数外面因为对那个类型是不可见的,即使不可以使用也没有问题啊,可能是我对这句话理解不够,我觉得作者的意思应该是说编译会通不过。不过我是使用的VS2005测试的,可能是后来模板的实现变化,因为在新标准里面是允许的,而作者的这篇文章写于2004年

我没有更好的解决方法了,如果你有的话,请告诉我吧o(∩_∩)o 。

顺便说一句,同样的问题在c#中怎么解决呢?其实你只需要调用x.length()就可以了,就是这么简单(我忘记了具体的语法,但是你应该明白的吧)。

但是有一点需要指出的是,在c#中一个数组的元素个数不会隐含在它的类型中,而是在运行时当创建数组时由您来决定它的元素个数。因此,就不要去尝试在编译时去获取这个信息了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值