The standard array-size macro that is often taught is
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))
or some equivalent formation. However, this kind of thing silently succeeds when a pointer is passed in, and gives results that can seem plausible at runtime until things mysteriously fall apart.
It's all-too-easy to make this mistake: a function that has a local array variable is refactored, moving a bit of array manipulation into a new function called with the array as a parameter.
So, the question is: is there a "sanitary" macro to detect misuse of the ARRAYSIZE
macro in C, preferably at compile-time? In C++ we'd just use a template specialized for array arguments only; in C, it seems we'll need some way to distinguish arrays and pointers. (If I wanted to reject arrays, for instance, I'd just do e.g. (arr=arr, ...)
because array assignment is illegal).
char a[MAGIC_STUFF(COMPLICATED(X, Z+FOO(G)))];
and not wanting to type that out again lower down. If the information is there and the toolset is there, use it.
#define SEND_FIXED_COMMAND(cmd) send_command((arr), sizeof (arr))
so as to avoid having to specify both the name of the array and the name of a constant giving the array's size.
Linux kernel uses a nice implementation of ARRAY_SIZE
to deal with this issue:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
with
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
and
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
Of course this is portable only in GNU C as it makes use of two instrinsics: typeof
operator and __builtin_types_compatible_p
function. Also it uses their "famous" BUILD_BUG_ON_ZERO
macro which is only valid in GNU C.
Assuming a compile time evaluation requirement (which is what we want), I don't know any portable implementation of this macro.
A "semi-portable" implementation (and which would not cover all cases) is:
#define ARRAY_SIZE(arr) \
(sizeof(arr) / sizeof((arr)[0]) + STATIC_EXP(IS_ARRAY(arr)))
with
#define IS_ARRAY(arr) ((void*)&(arr) == &(arr)[0])
#define STATIC_EXP(e) \
(0 * sizeof (struct { int ARRAY_SIZE_FAILED:(2 * (e) - 1);}))
With gcc
this gives no warning if argument is an array in -std=c99 -Wall
but -pedantic
would gives a warning. The reason is IS_ARRAY
expression is not an integer constant expression (cast to pointer types and subscript operator are not allowed in integer constant expressions) and the bit-field width in STATIC_EXP
requires an integer constant expression.
This version of ARRAYSIZE()
returns 0
when arr
is a pointer and the size when its a pure array
#include <stdio.h>
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAYSIZE(arr) (IS_ARRAY(arr) ? (sizeof(arr) / sizeof(arr[0])) : 0)
int main(void)
{
int a[5];
int *b = a;
int n = 10;
int c[n]; /* a VLA */
printf("%zu\n", ARRAYSIZE(a));
printf("%zu\n", ARRAYSIZE(b));
printf("%zu\n", ARRAYSIZE(c));
return 0;
}
Output:
5
0
10
As pointed out by Ben Jackson, you can force a run-time exception (dividing by 0)
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAYSIZE(arr) (sizeof(arr) / (IS_ARRAY(arr) ? sizeof(arr[0]) : 0))
Sadly, you can't force a compile-time error (the address of arg
must be compared at run-time)
IS_INDEXABLE(arg)
? As far as I can tell, this always returns non-zero
error: subscripted value is neither array nor pointer nor vector
With C11, we can differentiate arrays and pointers using _Generic
, but I have only found a way to do it if you supply the element type:
#define ARRAY_SIZE(A, T) \
_Generic(&(A), \
T **: (void)0, \
default: _Generic(&(A)[0], T *: sizeof(A) / sizeof((A)[0])))
int a[2];
printf("%zu\n", ARRAY_SIZE(a, int));
The macro checks: 1) pointer-to-A is not pointer-to-pointer. 2) pointer-to-elem is pointer-to-T. It evaluates to (void)0
and fails statically with pointers.
It's an imperfect answer, but maybe a reader can improve upon it and get rid of that type parameter!
_Generic(&(A), T(*)[sizeof(A) / sizeof((A)[0])]: sizeof(A) / sizeof((A)[0]))
This makes the second test unnecessary and I think the error message error: '_Generic' selector of type 'int **' is not compatible with any association
is better understandable than error: invalid use of void expression
. Sadly I still have no idea how to get rid of that type parameter. :-(
#define ARRAYSIZE(arr, T) _Generic(&(arr), T(*)[sizeof(arr)/sizeof(arr[0])]: sizeof(arr)/sizeof(arr[0]))
This creates an array pointer to an array of the specified type. If the passed parameter is not an array of the correct type or size, you'll get compiler errors. 100% portable standard C.
Modification of bluss's answer using typeof instead of a type parameter:
#define ARRAY_SIZE(A) \
_Generic(&(A), \
typeof((A)[0]) **: (void)0, \
default: sizeof(A) / sizeof((A)[0]))
typeof
is a GCC extension, so this code only works in GCC. If you are specific to GCC, then you could much better use something based on __builtin_types_compatible_p
or similar - that works in any case where this code would work, but it would additionally work in older versions of GCC or if an older standard was specified via the -std=
option.
Here's one possible solution using a GNU extension called statement expressions:
#define ARRAYSIZE(arr) \
({typedef char ARRAYSIZE_CANT_BE_USED_ON_POINTERS[sizeof(arr) == sizeof(void*) ? -1 : 1]; \
sizeof(arr) / sizeof((arr)[0]);})
This uses a static assertion to assert that sizeof(arr) != sizeof(void*)
. This has an obvious limitation -- you can't use this macro on arrays whose size happens to be exactly one pointer (e.g. a 1-length array of pointers/integers, or maybe a 4-length array of bytes on a 32-bit platform). But those particular instances can be worked around easily enough.
This solution is not portable to platforms which don't support this GNU extension. In those cases, I'd recommend just using the standard macro and not worry about accidentally passing in pointers to the macro.
Here's another one which relies on the gcc typeof extension:
#define ARRAYSIZE(arr) ({typeof (arr) arr ## _is_a_pointer __attribute__((unused)) = {}; \
sizeof(arr) / sizeof(arr[0]);})
This works by attempting to set up an identical object and initializing it with an array designated initializer. If an array is passed, then the compiler is happy. If pointer is passed the compiler complains with:
arraysize.c: In function 'main':
arraysize.c:11: error: array index in non-array initializer
arraysize.c:11: error: (near initialization for 'p_is_a_pointer')
= {};
: if you pass a pointer, you get "empty scalar initializer". This makes it portable to e.g. struct arrays.
= {};
didn't work for me :( - if I pass a simple int array, then I also get "error: empty scalar initializer". But I can pass arrays of ints, arrays of pointers or arrays of structs to the = 0;
version without difficulty.
{[0] = 0}
version does produce some warnings, however, about missing braces if you have an array of arrays or array of structs.
#define ARRAYSIZE(arr) ({typeof(arr) arr##_is_pointer = {}; sizeof(arr)/sizeof(arr[0]);})
. No designated initializer. This works properly for both int arrays and struct arrays with no warnings.
my personal favorite, tried gcc 4.6.3 and 4.9.2:
#define STR_(tokens) # tokens
#define ARRAY_SIZE(array) \
({ \
_Static_assert \
( \
! __builtin_types_compatible_p(typeof(array), typeof(& array[0])), \
"ARRAY_SIZE: " STR_(array) " [expanded from: " # array "] is not an array" \
); \
sizeof(array) / sizeof((array)[0]); \
})
/*
* example
*/
#define not_an_array ((char const *) "not an array")
int main () {
return ARRAY_SIZE(not_an_array);
}
compiler prints
x.c:16:12: error: static assertion failed: "ARRAY_SIZE: ((char const *) \"not an array\") [expanded from: not_an_array] is not an array"
__builtin_types_compatible_p
version fails for an array that's behind a const pointer (because const and non-const types don't match)
One more example to the collection.
#define LENGTHOF(X) ({ \
const size_t length = (sizeof X / (sizeof X[0] ?: 1)); \
typeof(X[0]) (*should_be_an_array)[length] = &X; \
length; })
Pros:
It works with normal arrays, variable-length arrays, multidimensional arrays, arrays of zero sized structs It generates a compilation error (not warning) if you pass any pointer, struct or union It does not depend on any of C11's features It gives you very readable error
Cons:
It depends on some of the gcc extensions: Typeof, Statement Exprs, and (if you like it) Conditionals It depends on C99 VLA feature
({ ... })
notation creates new scope. The only problem is when you use it like this: double length[234]; const size_t size = LENGTHOF(length);
. And you can always just duplicate (sizeof X / (sizeof X[0] ?: 1))
and don't use any temporary variable at all ;)
Awful, yes, but that works and it is portable.
#define ARRAYSIZE(arr) ((sizeof(arr) != sizeof(&arr[0])) ? \
(sizeof(arr)/sizeof(*arr)) : \
-1+0*fprintf(stderr, "\n\n** pointer in ARRAYSIZE at line %d !! **\n\n", __LINE__))
This will not detect anything at compile time but will print out an error message in stderr
and return -1
if it is a pointer or if the array length is 1.
==> DEMO <==
int arr2[2];
on my 64-bit box. In this case sizeof(arr)
and sizeof(&arr[0])
c are both equal to 8
sizeof(arr) != sizeof(&arr[0])
is a bad test. 1) It's misleading: On first glance one would probably assume, that sizeof(&arr[0])
somehow depends on arr
when in fact it almost never does. On all platforms I've ever known it's equivalent to sizeof(void*)
. (Do you happen to know a platform where sizeof(int*)!=sizeof(void*)
?) 2) As noted by @DigitalTrauma this error check easily leads to false positives. Why not use (((void *) &arg) == ((void *) arg))
? If you would change that, I could upvote - a runtime error message can at least for debug builds be very useful.
Success story sharing