Sizeof Array
So, you want to know how many elements there are in your array? This is a tricky subject.
Before we go any further, you should be familiar with the concepts discussed in c-strings and pointers . It is also worth your time to read through the tutorial on arrays .
A beginner will often try something along the lines of
size = sizeof( myarray )
(which is incorrect). The more experienced programmers will often go for the
C approach
(discussed below), because they do not know the
C++ approach
. Here we will explore both C and C++ approaches, and why the
C++ approach
is superior.
But first:
- Rationale
- The Basics
- SizeOfArray()
- Multiple Dimensions
Rationale ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
There are two broad classes of reasons to want to have some kind of method to return information about your array:
- You think the One Definition Rule should apply to the dimensions of each rank of an array.
The traditional, C way to do this is to declare the size of each rank (or dimension) using a named constant or macro:
1 2 3 4 5 6 7 8 9 10 11 12
#include <iostream> int main() { const size_t ROWS = 3; const size_t COLS = 4; double M[ ROWS ][ COLS ]; std::cout << "M is a " << ROWS << " by " << COLS << " matrix.\n"; return 0; }
This is actually a very simple, smart way to keep this information. Unless you tell it not to, the compiler will optimizeROWS
andCOLS
out of existence. It does, however, seem to use up a lot of space for a single array’s definition.
- You want to pass a multi-dimensional array with variable rank dimensions (the size of each dimension) to a function, and use it in the function normally.
C and C++ require you to specify the size of each dimension of an array except the first, or major, one. You can use this information when you pass such an array to a function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <iostream> using namespace std; void print( int a[][ 4 ], size_t n ) { for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < 4; j++) { cout << a[ i ][ j ] << " "; } cout << endl; } }
15 16 17 18 19 20 21 22 23 24 25 26 27 28
int main() { int M[ 3 ][ 4 ] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 } }; print( M, 3 ); return 0; }
The problem, of course, is that this limits the function considerably. While it does not matter how long the major dimension is, the exact size of the minor dimensions do matter. I cannot, for example, pass a 3 by 3 array to theprint()
function.
It is more desirable to be able to simply say that the array has two ranks (or dimensions), regardless of the size of each dimension, and still be able to use it like an array inside the function.
| |
We will deal with the second situation in the next couple of FAQs. So, with that in mind, let’s dig-in!
The Basics ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
Firstly, the sizeof()
operator does not give you the number of elements in an array, it gives you the number of bytes a thing occupies in memory. Hence:
千万注意了!!!!!!
不过,可以sizeof(xs) / sizeof(int)
注意xs的类型和除数中的变量类型一致。
| | sizeof( s ) = 6 sizeof( xs ) = 24 |
This causes some confusion, because the first time many beginners try to use
sizeof()
is often with a character string, and a
char
is
always
one byte long. An
int
, however, will be something different. (The actual size depends on your computer. On mine, an
int
is four bytes long, and six four-byte elements is 24 bytes total.)
Secondly, an array type will degenerate into a pointer type at every opportunity. The difference is that an array type knows all about the array, including its dimensions, whereas a pointer type only knows the address of the one thing it is pointing at. Here is an example to explain (you may have seen this, right?):
| | s = "Hello world!" The size of main()'s (s) is 13 The size of f()'s (s) is 4 The size of main()'s (&s) is 4 |
So, what happened?
In
main()
, the variable ‘s’ is an
array
, meaning it has all that useful information attached to it, like its size in memory. The example string is thirteen characters long (and hence, thirteen bytes long).
But when we pass it to the function
f()
, it is no longer an array, it is a
pointer
. Likewise, you get the same result by directly converting the array to a pointer (line 14). On my 32-bit operating system, a pointer is 4 bytes long. Your results might vary.
This is often surprising to beginners because the syntax
const char s[]
only means ‘an array’ when it is
not
a function argument. When it is, it is the same as saying
const char* s
,
even if you do specify a dimension
:
| | s[ 3 ] = "Hello world!" |
How we handle this differs between C and C++.
C++ ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
The first thing to do is not let the array degenerate into a pointer, if possible. The second is to get the
length
of the array instead of its
size in memory
.
C++ makes this simple using the power of templates. Here is a function that returns the length of the argument array.
| Yes, I know that the argument syntax here is confusingly wonky. What it says is that the (unnamed) argument is a reference (& ) to an array of N elements [ N ] of type const T .Some compilers (like the GCC) still choke on any effort to make the reference itself const, but it really doesn’t matter here, since the array is referenced as immutable anyway... | ||
Now, as long as I am still handling an array (and not a pointer), I can get its length by simply using the
SizeOfArray()
function:
| | s[] is 13 characters long. (That includes the null at the end.) |
The nice thing about this approach is that it will not compile if you try to use a pointer:
| |
The other nice thing is that, if your compiler does its job properly, the compiler will optimize that function out of existence and just plug the actual length of the array in your code where you call it. Neat, huh?
variable-length arrays ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
Immediately after authoring this page, someone on the forum tried to use
SizeOfArray()
like this:
| | <-- Line 2 is NON-STANDARD |
Not surprisingly, it didn’t work. Variable length arrays are not legal in C++. Compilers that allow it, like the GCC, do so with various hacks. But, since you already know the length of the array, why do you need to ask for it another way? Just use what you already know.
VLAs are legal in C99, and the C method works just fine with them.
C++11 ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
The
SizeOfArray()
function we have written so far works just fine. However, there is another way to get that information in C++11.
The <type_traits> header provides a number of classes for inspecting array types. Let’s use them:
| |
This does the same thing as what we already have, only it produces an unfamiliar error message because we use the fancy new
std::enable_if
type to force the return type of the function to
exist only if
the template argument ‘A’ is an array type. The
std::extent
type returns the dimension of a given rank of an array (in this case, it defaults to the first rank).
You can also use
std::extent
directly and skip the SizeOfArray() function:
| | five_primes[ 5 ] = { 2, 3, 5, 7, 11 }; |
The
std::extent
template returns zero if the object given it is not an array.
std::array ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
The new C++11
std::array
class provides a lot of STL sequence container information about your array, including its
size()
.
| | s[] = "Hello world!" s[] is 13 characters long. (That includes the null at the end.) |
The
std::array
class has several important characteristics, including the fact that it
will not
automatically degenerate to a pointer. (We had to use the
data()
member function to do that.)
However, as the dimension of the array (the size of its rank) is encoded into the type (as a template argument), we are stuck when we wish to pass variable -length arrays to a function.
C ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
In C, there is no way to have a compile-time safe way to do this. There is, however, a common macro to do it.
| |
It simply uses a little math and takes the size of the array in memory and divides that by the size of a single element in the array, which equals the number of elements in the array.
You use it very much the same: make sure you have an array (and not a pointer) and use the macro:
| | s[] is 13 characters long (That includes the null at the end.) |
The C++ way is superior simply because it catches any errors you make – you must pass an array to the function, whereas the existing C method does not. Some people will also complain that the C way uses a macro, but it really isn’t an issue here (because you must pass an array as argument, and nothing else). There is no compile-time check to enforce this, though.
Multiple Dimensions ![](https://i-blog.csdnimg.cn/blog_migrate/d2ebd50927fc7bc4f17acde738524f92.png)
Thus far we have only dealt with arrays with
one
dimension. It’s time to dig a little deeper.
The first observation we need to make is that the above methods already work on multidimensional arrays! The trick, of course, is that it will only return the length of the outermost dimension.
C++
| C
| Output
|
The next observation to make is that you can apply the same trick to the next dimension. Go ahead and add the following lines to the code above:
|
|
The final observation to make is that a single element of any array has a size of one element . This is a given. So, if you try to use
SIZEOF_ARRAY()
on an element (which is technically not an array), the answer you get will be
one
. The
SizeOfArray()
template function will not compile when given an element (but we can work this out with a little function overloading).
So, with that knowledge in hand, let us move on to the next couple of FAQs: Pass my (N-dimensional) array to a function? and Pass any (N-dimensional) array to a single function? .