C find static array size (preventing mistakes) [duplicate]

随声附和 提交于 2019-12-03 16:13:26

问题


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.


回答1:


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.




回答2:


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



回答3:


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



回答4:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!