C 查找静态数组大小(防止出错)

C find static array size (preventing mistakes)

查找静态数组的大小是一个常见的操作。参见: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

例如:

有没有更便携的方法来实现这个? (使用 Clang 当然很好,尽管我对一般可移植性很感兴趣……其他编译器也是如此)。

这似乎是一项很常见的任务,最好有一个可重复使用的便携式宏。

此宏适用于 clang 和 gcc(无论如何在我的测试中)。我几乎可以肯定没有便携式解决方案。

#define ARRAY_SIZE(a) \
    (({ static __typeof__(a) _aa; \
        static __typeof__(&(a)[0]) _pa = _aa; (void)_pa; }), \
           sizeof(a)/sizeof((a)[0]))

试试这个:

#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 的提议少。

这就是我使用的,带有 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

真的需要编译时断言吗?如果是,恐怕没有可移植的方法,您只能使用特定于实现的技巧使其在 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