查找静态数组的大小是一种常见操作。请参阅:C find static array size - sizeof(a) / sizeof((a)[0])
这可以包装成一个宏,例如:
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
然而,它可能会意外地传入常规指针。
例如:void func(SomeArray **foo) { int i = ARRAY_SIZE(foo); }
虽然它是有效的C,但通常最终会成为逻辑错误。
可以防止这个错误(利用每个处理器在零长度位字段上失败)。
#define ARRAY_SIZE(a) \
((sizeof(struct { int isnt_array : \
((const void *)&(a) == &(a)[0]); }) * 0) + \
(sizeof(a) / sizeof(*(a))))
我发现这个宏可以与GCC一起使用,但对于间接引用的成员而言,它与Clang失败了。与error: expression is not an integer constant expression
例如:
char word[8]; int i = ARRAY_SIZE(word);
好的。struct Bar { word[8]; }
void func(struct Bar *foo) { int i = ARRAY_SIZE(foo->word); }
失败。有更便携的方法来实现这个吗? (与Clang一起工作当然很好,虽然我对一般可移植性感兴趣......其他编译器也是如此。)
这似乎是一项普遍的任务,拥有一个可重复使用的便携式宏。
答案 0 :(得分:2)
试试这个:
#define ASSERT_ARRAY(a) \
sizeof(char[1-2*__builtin_types_compatible_p(__typeof__(a), __typeof__(&(a)[0]))])
#define ARRAY_SIZE(a) \
(ASSERT_ARRAY(a)*0 + sizeof(a)/sizeof((a)[0]))
它不可移植,但适用于gcc
和clang
,并且副作用少于n.m。的提案。
答案 1 :(得分:1)
对于clang和gcc,这个宏(无论如何我的测试)都有效。我几乎可以肯定没有便携式解决方案。
#define ARRAY_SIZE(a) \
(({ static __typeof__(a) _aa; \
static __typeof__(&(a)[0]) _pa = _aa; (void)_pa; }), \
sizeof(a)/sizeof((a)[0]))
答案 2 :(得分:1)
我使用的是C和C ++的解决方案。
对我来说,两者都要在VLA上工作。
#ifndef __cplusplus
int _ptr_used_(void) __attribute__((error("Pointer used in place of array") ));
#define ARRAY_SIZEOF(arr) ( \
__builtin_types_compatible_p(typeof(arr), typeof((arr)[0])*) \
? _ptr_used_() \
: sizeof(arr)/sizeof((arr)[0]) \
)
#else
/// A type that exists
struct _true_ {};
template <bool constant>
struct is_an_array
{
/// Used when a constant sized (non-VLA) object is passed in
/// only allow arrays past
template<class B, size_t n>
static _true_ test( B(&)[n] );
};
template <>
struct is_an_array<false>
{
/// This happens only for VLAs; force decay to a pointer to let it work with templates
template <class B>
static _true_ test(B *n);
};
# define ARRAY_SIZEOF(arr) ({ typedef decltype(is_an_array<static_cast<bool>(__builtin_constant_p(sizeof(arr)))>::test(arr)) type; sizeof(arr) / sizeof((arr)[0]); })
#endif
答案 3 :(得分:1)
你真的需要编译时断言吗?如果是的话,我担心没有可移植的方式,你只能通过特定于实现的技巧将它用于Clang和GCC或其他编译器。
但是如果您决定寻求可移植性,则可以使用运行时错误(这可能同样有效,具体取决于您的测试策略)。假设您有错误报告功能void error(char *errorText)
。宏可能看起来像这样(未经测试,但我希望你能得到这个想法):
#ifdef DEBUG /* Place your debug-mode-flag macro here.
You won't want the extra branch in the release build */
#define ARRAY_SIZE(a) \
((const void *)&(a) == &(a)[0]) ? \
(sizeof(a) / sizeof(*(a))) : (error("Tried to treat pointer as array!"), 0)
#else
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#endif