29

Is there a standard function in C that would return the length of an array?

jdehaan
  • 19,700
  • 6
  • 57
  • 97
user133466
  • 3,391
  • 18
  • 63
  • 92
  • As I think, it wasn't mentioned on this page yet: [Array-size macro that rejects pointers](https://stackoverflow.com/q/19452971/2770331) – T S Mar 21 '21 at 14:54

7 Answers7

70

Often the technique described in other answers is encapsulated in a macro to make it easier on the eyes. Something like:

#define COUNT_OF( arr) (sizeof(arr)/sizeof(0[arr]))

Note that the macro above uses a small trick of putting the array name in the index operator ('[]') instead of the 0 - this is done in case the macro is mistakenly used in C++ code with an item that overloads operator[](). The compiler will complain instead of giving a bad result.

However, also note that if you happen to pass a pointer instead of an array, the macro will silently give a bad result - this is one of the major problems with using this technique.

I have recently started to use a more complex version that I stole from Google Chromium's codebase:

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

In this version if a pointer is mistakenly passed as the argument, the compiler will complain in some cases - specifically if the pointer's size isn't evenly divisible by the size of the object the pointer points to. In that situation a divide-by-zero will cause the compiler to error out. Actually at least one compiler I've used gives a warning instead of an error - I'm not sure what it generates for the expression that has a divide by zero in it.

That macro doesn't close the door on using it erroneously, but it comes as close as I've ever seen in straight C.

If you want an even safer solution for when you're working in C++, take a look at Compile time sizeof_array without using a macro which describes a rather complex template-based method Microsoft uses in winnt.h.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
14

No, there is not.

For constant size arrays you can use the common trick Andrew mentioned, sizeof(array) / sizeof(array[0]) - but this works only in the scope the array was declared in.
sizeof(array) gives you the size of the whole array, while sizeof(array[0]) gives you the size of the first element.
See Michaels answer on how to wrap that in a macro.

For dynamically allocated arrays you either keep track of the size in an integral type or make it 0-terminated if possible (i.e. allocate 1 more element and set the last element to 0).

Community
  • 1
  • 1
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
  • 13
    Note that `sizeof(array)/sizeof(array[0])` only works in the same scope that the array was declared. We can't pass `array` to a function and expect that code to work. – Chris Lutz Oct 21 '09 at 05:04
  • 1
    Not only it works for constant size arrays, but also for variable length arrays in C99. In that case, though, the `sizeof` is evaluated at runtime. – Johannes Schaub - litb Mar 05 '10 at 03:01
4
sizeof array / sizeof array[0]
nobody
  • 19,814
  • 17
  • 56
  • 77
  • 12
    You should explain that this only works for fixed size arrays where the size is known at compile time. – John Kugelman Oct 21 '09 at 04:58
  • 1
    You could shorten this further by using `*array` :-) – Frerich Raabe Sep 07 '16 at 12:13
  • 2
    The expression also works for VLAs (variable length arrays) where the size is determined at run-time. It does not work for dynamically allocated arrays (via `malloc()` etc). It also fails on 'arrays' in function parameter lists — but they aren't arrays anyway. – Jonathan Leffler Apr 12 '17 at 23:51
2

The number of elements in an array x can be obtained by:

sizeof(x)/sizeof(x[0])

You need to be aware that arrays, when passed to functions, are degraded into pointers which do not carry the size information. In reality, the size information is never available to the runtime since it's calculated at compile time, but you can act as if it is available where the array is visible (i.e., where it hasn't been degraded).

When I pass arrays to a function that I need to treat as arrays, I always ensure two arguments are passed:

  • the length of the array; and
  • the pointer to the array.

So, whilst the array can be treated as an array where it's declared, it's treated as a size and pointer everywhere else.

I tend to have code like:

#define countof(x) (sizeof(x)/sizeof(x[0]))
: : :
int numbers[10];
a = fn (countof(numbers),numbers);

then fn() will have the size information available to it.

Another trick I've used in the past (a bit messier in my opinion but I'll give it here for completeness) is to have an array of a union and make the first element the length, something like:

typedef union {
    int len;
    float number;
} tNumber;
tNumber number[10];
: : :
number[0].len = 5;
a = fn (number);

then fn() can access the length and all the elements and you don't have to worry about the array/pointer dichotomy.

This has the added advantage of allowing the length to vary (i.e., the number of elements in use, not the number of units allocated). But I tend not to use this anymore since I consider the two-argument array version (size and data) better.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • That `union` trick is neat, but I'd much rather use the `struct` hack (or a non-hack version of the same idea). – Chris Lutz Oct 21 '09 at 05:26
2

I created a macro that returns the size of an array, but yields a compiler error if used on a pointer. Do however note that it relies on gcc extensions. Because of this, it's not a portable solution.

#define COUNT(a) (__builtin_choose_expr( \
                  __builtin_types_compatible_p(typeof(a), typeof(&(a)[0])), \
                  (void)0, \
                  (sizeof(a)/sizeof((a)[0]))))

int main(void)
{
    int arr[5];
    int *p;
    int x = COUNT(arr);
//  int y = COUNT(p);
}

If you remove the comment, this will yield: error: void value not ignored as it ought to be

klutt
  • 30,332
  • 17
  • 55
  • 95
1

The simple answer, of course, is no. But the practical answer is "I need to know anyway," so let's discuss methods for working around this.

One way to get away with it for a while, as mentioned about a million times already, is with sizeof():

int i[] = {0, 1, 2};
...
size_t i_len = sizeof(i) / sizeof(i[0]);

This works, until we try to pass i to a function, or take a pointer to i. So what about more general solutions?

The accepted general solution is to pass the array length to a function along with the array. We see this a lot in the standard library:

void *memcpy(void *s1, void *s2, size_t n);

Will copy n bytes from s1 to s2, allowing us to use n to ensure that our buffers never overflow. This is a good strategy - it has low overhead, and it actually generates some efficient code (compare to strcpy(), which has to check for the end of the string and has no way of "knowing" how many iterations it must make, and poor confused strncpy(), which has to check both - both can be slower, and either could be sped up by using memcpy() if you happen to have already calculated the string's length for some reason).

Another approach is to encapsulate your code in a struct. The common hack is this:

typedef struct _arr {
  size_t len;
  int arr[0];
} arr;

If we want an array of length 5, we do this:

arr *a = malloc(sizeof(*a) + sizeof(int) * 5);
a->len = 5;

However, this is a hack that is only moderately well-defined (C99 lets you use int arr[]) and is rather labor-intensive. A "better-defined" way to do this is:

typedef struct _arr {
  size_t len;
  int *arr;
} arr;

But then our allocations (and deallocations) become much more complicated. The benefit of either of these approaches is, of course, that now arrays you make will carry around their lengths with them. It's slightly less memory-efficient, but it's quite safe. If you chose one of these paths, be sure to write helper functions so that you don't have to manually allocate and deallocate (and work with) these structures.

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • 3
    "Struct hack" is a hack, but there's no reason to make it more hackish than it needs to be. Arrays of size 0 were (and are) always explictly illegal in C. The proper struct declaration for "struct hack" uses array of size 1. The size for `malloc` is traditionally calculated as `offsetof(arr, arr) + n * sizeof(int)` (so it doesn't matter what size is used in the array declaration). – AnT stands with Russia Oct 21 '09 at 05:29
  • They are illegal, but many older C compilers supported them (from laziness, no doubt), and I've seen `arr[0]` many times (with cringes). I've never seen the size calculated with `offsetof()` though that is a nice way to ensure it. And I disagree. Hacks should loudly scream "This is a hack! Please improve this code when you get the chance!" – Chris Lutz Oct 21 '09 at 05:42
  • Well, I'd say that the beauty of "struct hack" is that it doesn't violate any constraints. The program must compile fine. "Struct hack" always relied on practical "definedness" of theoretical undefined behavior. And, as we all know, life often makes us rely on that. Which is why we love and cherish that "struct hack". But the `[0]` version make a drastic qualitative change to all that. It introduces a constraint violation, leading to the possible compilation failure. It is hard to love something that doesn't even compile. (Yes, I know that some compilers allowed and still allow `[0]` arrays.) – AnT stands with Russia Oct 21 '09 at 05:51
  • 1
    The "hack" is legal in C99, with a much more hack-y looking syntax. And though I agree that legal hacks are good, if they're fully legal, they're not really hacks. (And for what it's worth, the `int arr[0]` compiles on GCC 4.0 with -Wall -Werror -Wextra without a peep. It can't be _that_ illegal.) – Chris Lutz Oct 21 '09 at 06:13
  • Chris Lutz: try -pedantic as well, arr[0] as the last member of a struct is a documented GCC extension :) – zwol Jul 21 '10 at 23:55
0

If you have an object a of array type, the number of elements in the array can be expressed as sizeof a / sizeof *a. If you allowed your array object to decay to pointer type (or had only a pointer object to begin with), then in general case there's no way to determine the number of elements in the array.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765