This question already has an answer here:
- Array-size macro that rejects pointers 9 answers
Finding the size of a static array is a common operation. see: C find static array size - sizeof(a) / sizeof((a)[0])
This can be wrapped into a macro, eg:
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
however its possible to accidentally pass in a regular pointer.
eg: void func(SomeArray **foo) { int i = ARRAY_SIZE(foo); }
While its valid C, but often ends up being a logical error.
Its possible to prevent this mistake (taking advantage of per-processor to fail on a zero length bit-field).
#define ARRAY_SIZE(a) \
((sizeof(struct { int isnt_array : \
((const void *)&(a) == &(a)[0]); }) * 0) + \
(sizeof(a) / sizeof(*(a))))
I've found this macro works with GCC, but fails with Clang, for indirectly referenced members. with error: expression is not an integer constant expression
eg:
char word[8]; int i = ARRAY_SIZE(word);
ok.struct Bar { word[8]; }
void func(struct Bar *foo) { int i = ARRAY_SIZE(foo->word); }
fails.
Is there a more portable way to to implement this? (working with Clang is good of course though Im interested in general portability... other compilers too).
This seems such a common task that it would be good to have a re-usable portable macro.
Try this:
#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]))
It is not portable, but works with both gcc
and clang
and has fewer side effects than n.m.'s proposal.
This is what I use, with a solution for both C and C++.
It was a requirement for me that both work even on VLAs.
#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
Do you really need a compile-time assertion? If yes, I'm afraid there's no portable way, you can only get this to work on Clang and GCC, or some other compiler, with implementation-specific tricks.
But if you decide to go for portability, you can use a run-time error (which can be just as effective, depending on your testing strategy). Suppose you have an error-reporting function void error(char *errorText)
. The macro could look something like this (untested, but I hope you get the idea):
#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
This macro works (in my tests anyway) for clang and gcc. I'm almost sure there's no portable solution.
#define ARRAY_SIZE(a) \
(({ static __typeof__(a) _aa; \
static __typeof__(&(a)[0]) _pa = _aa; (void)_pa; }), \
sizeof(a)/sizeof((a)[0]))
来源:https://stackoverflow.com/questions/29924710/c-find-static-array-size-preventing-mistakes