43

I've seen several macros for array length floating around:

From this question:

  • #define length(array) (sizeof(array)/sizeof(*(array)))
  • #define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
  • #define SIZE(array, type) (sizeof(array) / (sizeof(type))

And Visual Studio's _countof:

#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))

What I'd like to know is:

  1. What's the difference between those using array[0] and *array?
  2. Why should either be preferred?
  3. Do they differ in C++?
Community
  • 1
  • 1
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526

3 Answers3

81

Here's a better C version (from Google's Chromium project):

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

It improves on the array[0] or *array version by using 0[array], which is equivalent to array[0] on plain arrays, but will fail to compile if array happens to be a C++ type that overloads operator[]().

The division causes a divide-by-zero operation (that should be caught at compile time since it's a compile-time constant expression) for many (but not all) situations where a pointer is passed as the array parameter.

See Is there a standard function in C that would return the length of an array? for more details.

There's a better option for C++ code. See Compile time sizeof_array without using a macro for details.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 14
    That's incredibly cryptic just to guard against being used wrongly in C++, considering that something else should be used in C++ anyway. – Karl Knechtel Dec 11 '10 at 07:19
  • 2
    You're right that this shouldn't be used in C++, but the bit that provides some safety specifically for C++ isn't really too cryptic (it's just the unusual indexing). The really cryptic part guards against some types of misuse in C that is actually pretty hard to guard against (and the macro doesn't even perfect guard against this misuse). The misuse it guards against (passing a pointer instead of an array argument) occurs often enough that it pays to have the complexity. Wrapped up in a macro, the complexity isn't too much of an issue (and this macro is less cryptic than many I come across). – Michael Burr Dec 11 '10 at 07:45
  • 1
    What the hell type is `0[x]`? You can do that? – Matt Joiner Dec 11 '10 at 10:05
  • 3
    @Matt: the `[]` operator is commutative (by virtue of it being defined in terms of pointer arithmetic). However that part of the macro is the less important 'safety' - I'm sorry that it seems to be getting the attention. – Michael Burr Dec 11 '10 at 19:11
  • As noted in the linked source , the second half of this check only catches some cases. (In particular it doesn't catch `x` being a `char *`, which is a common use case) – M.M Apr 11 '17 at 15:17
  • Since there is a better option for C++ code, that should probably be used: `#ifdef __cplusplus ... version for C++ .... #else ... version for C ... #endif` – Kaz Jun 02 '17 at 21:29
18
  1. What's the difference between those using array[0] and *array?
  2. Why should either be preferred?
  3. Do they differ in C++?

(1) No difference in C. No difference for an actual raw array in C++.

(2) No technical grounds to prefer one or the other, but newbies might be confused by the pointer dereference.

(3) In C++ you would normally not use the macro, because it's very unsafe. If you pass in a pointer instead of an actual raw array, code will compile but yield incorrect result. So in C++ you would/should instead use a function template, like …

#include <stddef.h>

typedef ptrdiff_t Size;

template< class Type, Size n >
Size countOf( Type (&)[n] ) { return n; }

This only accepts actual raw array as argument.

It's part of a triad of functions startOf, endOf and countOf that it's very convenient to define so that they can be applied to both raw arrays and standard library containers. As far as I know this triad was first identified by Dietmar Kuehl. In C++0x startOf and endOf will most probably be available as std::begin and std::end.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 2
    +1: Interesting! Is there a reason to use `` rather than ``? – Jonathan Leffler Dec 11 '10 at 06:42
  • Not that I can think of; and I can think of several reasons to prefer . – Karl Knechtel Dec 11 '10 at 07:18
  • 1
    For what little it's worth, here's my take on the `` vs. `` reasoning (note: I prefer the old-fashioned C variation myself): http://stackoverflow.com/questions/2118422/scope-of-c-libraries-in-c-x-h-vs-cx/2118718#2118718 – Michael Burr Dec 11 '10 at 07:34
  • 1
    I'm in the `stddef.h` even in C++ camp too. Trying to deprecate C standard library is ridiculous. – Matt Joiner Dec 11 '10 at 10:03
  • 1
    Worth noting that in general there is a difference between `*a` and `a[0]`, which shows up when `a` is a pointer to an incomplete type. This doesn't influence the `sizeof` though, since in that case `sizeof` would be invalid anyway. – Johannes Schaub - litb Dec 11 '10 at 10:47
  • template countOf does not work if array is a member of a class. To make it working with class members, it should be extended with another template parameter `template` `static inline constexpr size_t countOf(T (C::*)[N]) { return N; }` – hutorny Sep 08 '15 at 17:57
  • @hutorny: There is a case where the function template doesn't work as a given, but that problematic case is not for member of a class, and it's not for current standard C++. If you want to use it for an array type where you don't have an instance, whether in a class or not, then you can either pass it a pseudo-instance via e.g. `std::declval`, or you can use the standard library's array type dimension thing (I forget the name, sorry, I've never needed it). – Cheers and hth. - Alf Sep 08 '15 at 22:37
4

1) Nothing, the value of an array is a pointer to it's first element. So *array == array[0]
2) Personal Preference
3) No

Note that this macro wont work if called inside a function where the array is passed as a parameter into the function. This is because the array object passed "decays" into a pointer rather than a deep copy.

SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • Yes, it may effectively work as `sizeof(void*)/sizeof(array[0])`, which - in contemporary reality - means 8, 4, 2, 1, or 0. ;) Anyway - nothing can help you in such a function at compile time - you can never know who calls it, and with how large an array. – Tomasz Gandor Oct 22 '14 at 22:20