Linux 内核使用了一个很好的实现ARRAY_SIZE
来处理这个问题:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
和
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
和
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
当然,这仅在 GNU C 中是可移植的,因为它使用了两个内在函数:
typeof
运算符和__builtin_types_compatible_p
函数。它还使用他们的“著名”BUILD_BUG_ON_ZERO
宏,该宏仅在 GNU C 中有效。
假设编译时评估要求(这是我们想要的),我不知道这个宏的任何可移植实现。
“半便携式”实现(并且不会涵盖所有情况)是:
#define ARRAY_SIZE(arr) \
(sizeof(arr) / sizeof((arr)[0]) + STATIC_EXP(IS_ARRAY(arr)))
和
#define IS_ARRAY(arr) ((void*)&(arr) == &(arr)[0])
#define STATIC_EXP(e) \
(0 * sizeof (struct { int ARRAY_SIZE_FAILED:(2 * (e) - 1);}))
gcc
如果参数是一个数组,则不会发出警告,但-std=c99 -Wall
会-pedantic
发出警告。原因是IS_ARRAY
表达式不是整数常量表达式(整数常量表达式中不允许转换为指针类型和下标运算符)并且位域宽度STATIC_EXP
需要整数常量表达式。