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
例如:
char word[8]; int i = ARRAY_SIZE(word);
好的。
struct Bar { word[8]; }
void func(struct Bar *foo) { int i = ARRAY_SIZE(foo->word); }
失败。
有没有更便携的方法来实现这个? (使用 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]))
它不可移植,但可以与 gcc
和 clang
一起使用,并且副作用比 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
查找静态数组的大小是一个常见的操作。参见: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 当然很好,尽管我对一般可移植性很感兴趣……其他编译器也是如此)。
这似乎是一项很常见的任务,最好有一个可重复使用的便携式宏。
此宏适用于 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]))
它不可移植,但可以与 gcc
和 clang
一起使用,并且副作用比 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