浅析_countof宏

简介:只要知道这几个概念就不难理解_countof宏了,那就是函数指针、对数组的引用、函数的返回类型为数组类型

      char (*pfn)();//返回类型为char,参数为空的函数指针类型

      char (*pfn)()[5];//返回类型为char,大小为5的数组,参数为空;注意,因为不允许返回类型为数组类型,所以无法定义该函数指针类型所对应的函数实体。但能定义函数指针。

      char (&array)[5];//对类型为char,大小为5的数组的引用,因为是对数组的引用,所以当对_countof宏传入的是一个char*指针变量,那么将编译报错。


[plain]  view plain  copy
  1. #ifndef _cplusplus  
  2. #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))  
  3. #else  
  4. extern "C++"  
  5. {  
  6. template <typename _CountofType, size_t _SizeOfArray>  
  7. char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];  
  8. #define _countof(_Array) sizeof(*__countof_helper(_Array))  
  9. }  
  10. #endif  
上面是vc中_countof宏的实现。根据条件编译宏的定义,当用在c的代码中,使用的是最常用的方法即 sizeof(_Array) / sizeof(_Array[0])的方式.但用在c++代码时,这个宏的定义就有点让人丈二摸不着头脑了:

[cpp]  view plain  copy
  1. template <typename _CountofType, size_t _SizeOfArray>  
  2. char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];  
  3. #define _countof(_Array) sizeof(*__countof_helper(_Array))  
 

 
首先,辅助函数_countof_helper是一个指向一个模板函数的函数指针的声明,乍看起来让人不易弄明白是什么意思,我们可以采用分而治之的方法来解析这个声明。 

我们可以暂时忽略掉最外面的一对大括号中的那一大坨代码,,我们可以用typedef来让它看起来更直观一些:

[cpp]  view plain  copy
  1. typedef char array_t [_SizeOfArray];  
  2. template <typename _CountofType, size_t _SizeOfArray>  
  3. array_t _countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]);  
现在稍微清晰了点吧?!不过就是一个返回类型为 有_SizeOfArray个元素的char数组 的函数指针声明么。

好了,现在该看看这个函数声明中的参数了,UNALIGNED这个宏其实定义为空,我们可以直接pass过去,所以函数参数的类型如下:

[cpp]  view plain  copy
  1. _CountofType (&_Array)[_SizeOfArray];  
 

 

这行代码又表示的是什么意思呢?我们可以再typedef一下就知道了:

[cpp]  view plain  copy
  1. typdef _CountofType (&refarray_t) [_SizeOfArray]; // 有_SizeOfArray个元素的数组的引用!  
[cpp]  view plain  copy
  1. array_t _countof_helper (refarray_t _Array);  

一目了然!_countof_helper其实是一个指向返回值类型为char数组,参数类型为_CountofType数组的引用的函数的指针罢了!

现在该看看_countof这个宏的定义了

[cpp]  view plain  copy
  1. #define _countof(_Array) sizeof(*__countof_helper(_Array))  

当我们调用_countof(arr)时,其实我们是在执行sizeof(*__countof_helper(arr))这个表达式,上面我们可以看到,__countof_helper是一个函数指针的声明,但它的定义又在哪里呢?

真相是,__countof_helper这个函数指针根本就没有被赋值!很多童鞋们可能就会纳闷了,既然__countof_helper这个函数指针只有声明没有赋值,那么当我们的代码调用_countof(arr)时,程序应该运行就会出错了?!

庆幸的是,这些担心是多余的,因为_countof_helper压根就没有机会被调用!我们可以看看_countof_helper是在什么样的上下文中被使用的:

[cpp]  view plain  copy
  1. sizeof(*__countof_helper(_Array))  
看到了么,是在sizeof中调用的,sizeof操作符有一个非常伟大的特性,那就是它是在编译期获取对象类型信息来产生结果的,既然在编译期获得结果,那么__countof_helper当然就没有机会执行了。童鞋们可能又会疑惑了,那么既然__countof_helper压根就没有被调用过,那么又为什么要声明它呢?

其实,__countof_helper函数指针存在的意义并不在于被调用,而是在于提供类型信息!这就是sizeof的强大之处了,我们可以回顾一下上面分析中,__countof_helper指针指向的函数,其返回类型为有_SizeofArray个元素的的char数组,所以这个sizeof返回的值也就是_SizeofArray了。

我们再来回顾一下,__countof_helper这个所指函数的参数类型为拥有_SizeofArray个元素的的数组的引用,数组元素的类型则是_CountOfType。而_SizeofArray和_CountOfType都是模板自动为我们推演出来的模板参数,当我们将一个数组传递给这个模板函数的时候,_CountOfType是这个数组的元素数目,而_CountOfType则是数组元素的类型了,所以_counof这个宏返回的_SizeofArray就是数组的真实元素数目了,而这所有的工作都是在编译期完成的,没有任何运行期开销。这就是c++模板的无与伦比的强大之处了。

最后的最后,我们要提一个小小的问题了,虽然上面这个模板函数指针的确很强大,但 sizeof(array)/sizeof(array[0]) 这种做法似乎也能实现我们想要的结果,而且也没有任何运行期性能开销啊?!为什么非要把事情弄得这么复杂呢?

我们可以看看如下一段小程序:

[cpp]  view plain  copy
  1. char szHello[] = "hello";  
  2. char *ptr = szHello;  
  3. #define arr_size(_ptr_) sizeof(_ptr_) / sizeof(_ptr_[0])  
  4. cout << arr_size(ptr) << endl;  

请问arr_size返回的是ptr所指字符串的长度么?

答案是否定的,我们可以分析下,当传入的_ptr_为数组时,arr_size产生的结果正是我们所要的数组元素数,但当_ptr_为普通的指针时,sizeof(_ptr_)其值在32位平台上必为4,即为指针的大小,而sizeof(_ptr_[0])则是_ptr_所指元对象的大小(在这里就是char的大小,这里ptr只是指向char的指针)。故而会产生与我们预期的风马牛不相及的结果。

好吧,让我们用更复杂的_countof宏试试,结果会如何呢?

[cpp]  view plain  copy
  1. char szHello[] = "hello";  
  2. char *ptr = szHello;  
  3. cout << _countof(ptr);  

VC2005输出窗口给出的错误消息:

[plain]  view plain  copy
  1. error C2784: “char (*__countof_helper(_CountofType (&)[_SizeOfArray]))[_SizeOfArray]”: 无法从“char *”为“_CountofType (&)[_SizeOfArray]”推导 模板 参数  

“啊哈!没编译通过!看来_countof也没我们想象的那么强大么!”

不得不说_countof宏并不是全能的,它上面的arr_size宏一样,只能应用在数组类型,而对于从数组退化而成的指针则是无能为力了。不过,_countof宏相对于arr_size宏有一个优势就是,它可以在编译期识别出参数为普通指针的异常情况,并且会产生编译错误告诉用户——你丫的传个毛指针!而arr_size则是一声不响的在运行时产生错误的结果,所以程序也就一声不响地越跑越不靠谱!所以综合而言,使用_countof宏更为安全!

原文地址:https://blog.csdn.net/hanzz2007/article/details/6667209


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值