Common array length macro for C? [duplicate]

2019-01-06 10:02发布

问题:

This question already has an answer here:

  • Is there a standard function in C that would return the length of an array? 6 answers
  • Array-size macro that rejects pointers 9 answers

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++?

回答1:

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.



回答2:

  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.,



回答3:

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.