C找到静态数组大小(防止错误)

时间:2015-04-28 16:18:47

标签: c arrays c-preprocessor

查找静态数组的大小是一种常见操作。请参阅: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一起工作当然很好,虽然我对一般可移植性感兴趣......其他编译器也是如此。)

这似乎是一项普遍的任务,拥有一个可重复使用的便携式宏。

4 个答案:

答案 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]))

它不可移植,但适用于gccclang,并且副作用少于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