简介:只要知道这几个概念就不难理解_countof宏了,那就是函数指针、对数组的引用、函数的返回类型为数组类型
char (*pfn)();//返回类型为char,参数为空的函数指针类型
char (*pfn)()[5];//返回类型为char,大小为5的数组,参数为空;注意,因为不允许返回类型为数组类型,所以无法定义该函数指针类型所对应的函数实体。但能定义函数指针。
char (&array)[5];//对类型为char,大小为5的数组的引用,因为是对数组的引用,所以当对_countof宏传入的是一个char*指针变量,那么将编译报错。
- #ifndef _cplusplus
- #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
- #else
- extern "C++"
- {
- template <typename _CountofType, size_t _SizeOfArray>
- char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
- #define _countof(_Array) sizeof(*__countof_helper(_Array))
- }
- #endif
- template <typename _CountofType, size_t _SizeOfArray>
- char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
- #define _countof(_Array) sizeof(*__countof_helper(_Array))
我们可以暂时忽略掉最外面的一对大括号中的那一大坨代码,,我们可以用typedef来让它看起来更直观一些:
- typedef char array_t [_SizeOfArray];
- template <typename _CountofType, size_t _SizeOfArray>
- array_t _countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]);
好了,现在该看看这个函数声明中的参数了,UNALIGNED这个宏其实定义为空,我们可以直接pass过去,所以函数参数的类型如下:
- _CountofType (&_Array)[_SizeOfArray];
这行代码又表示的是什么意思呢?我们可以再typedef一下就知道了:
- typdef _CountofType (&refarray_t) [_SizeOfArray]; // 有_SizeOfArray个元素的数组的引用!
- array_t _countof_helper (refarray_t _Array);
一目了然!_countof_helper其实是一个指向返回值类型为char数组,参数类型为_CountofType数组的引用的函数的指针罢了!
现在该看看_countof这个宏的定义了
- #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是在什么样的上下文中被使用的:
- sizeof(*__countof_helper(_Array))
其实,__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]) 这种做法似乎也能实现我们想要的结果,而且也没有任何运行期性能开销啊?!为什么非要把事情弄得这么复杂呢?
我们可以看看如下一段小程序:
- char szHello[] = "hello";
- char *ptr = szHello;
- #define arr_size(_ptr_) sizeof(_ptr_) / sizeof(_ptr_[0])
- cout << arr_size(ptr) << endl;
请问arr_size返回的是ptr所指字符串的长度么?
答案是否定的,我们可以分析下,当传入的_ptr_为数组时,arr_size产生的结果正是我们所要的数组元素数,但当_ptr_为普通的指针时,sizeof(_ptr_)其值在32位平台上必为4,即为指针的大小,而sizeof(_ptr_[0])则是_ptr_所指元对象的大小(在这里就是char的大小,这里ptr只是指向char的指针)。故而会产生与我们预期的风马牛不相及的结果。
好吧,让我们用更复杂的_countof宏试试,结果会如何呢?
- char szHello[] = "hello";
- char *ptr = szHello;
- cout << _countof(ptr);
VC2005输出窗口给出的错误消息:
- 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