Arrays are not the same as pointers. There are some important distinctions:
sizeof(pointer)
is always the same, regardless of the
number of elements the pointer addresses, or the type of those elements. sizeof(array
depends on both the size of the array, and the
element type.
char x[] = "abcdefghijklmnopqrstuvwxyz"; char* y = x; // now we can do pointer arithmatic with y
int main() { int x[3]; int *y; int z; y = x; // OK, because there's an implict conversion from int[] to int* x = y; // ERROR z = *x; // Doesn't cause a crash z = *y; // Crash if y doesn't point to anything y++; // OK, you can increment a pointer x++; // ERROR, you can't do so to an array. }
The point on the sizeof an array deserves further discussion, because it is a very important point. Arrays are assigned space at compile-time, hence stack space is reserved for them. Here is a schematic of an array corresponding to the code:
char b[3][5];This two-dimensional array is actually an array of one-dimensional arrays. In this example,
b
is an array of arrays of 5
characters. The length of b
is 3, so b
contains
3 arrays, each containing of 5 characters.
The size of each element in the array will be
5*sizeof(char) = 5, because the arrays of 5 characters are physically
stored in b
Note that this is vastly different to an array of dynamically pointers.
This example is also illustrated with some code: Download Code
#include <iostream> int main() { char a[] = {2,3,4}; char b[3][5]; char *x; char ch; std::cout << "Sizeof char[3]: " << sizeof(a) << std::endl; // 3*sizeof(int) std::cout << "size: char b[3][5]: " << sizeof(b) << std::endl; // 3 * 5 * sizeof(char) std::cout << "sizeof b[0]: " << sizeof(b[0]) << std::endl; // 5 * sizeof(char) x = b[0]; ch = 'a'; // This shows that b is a contiguous block of memory. for (int i = 0; i < sizeof(b); ++i) *x++ = ch++; for (int i = 0; i < 3; ++i ) { std::cout << "b[" << i << "] "; for ( int j = 0; j < 5; ++j ) std::cout << b[i][j]; std::cout << std::endl; } }
Note that this is very different to a dynamic 2-dimensional array allocated like this:
char** x = new char*[m]; for (int i = 0; i < m; ++i ) x[i] = new char[n];The above code results in a picture like this:
The misconception that they are the same stems from the fact that the following function signatures are in fact exactly the same:
void foo(char* x); void foo(char x[]);
In both examples, the parameter x
is a pointer, not an array.
This is really quite hard to believe, and at first, I didn't believe it
myself. How can something that's declared as an array actually be a pointer
? There are a number of means at our disposal that we've already established,
that we could use to verify that x is indeed a pointer, even when it's
declared as an array in the function signature:
In case even that is not sufficiently convincing for the sceptical reader, we can also use a C++ feature called RTTI, or Runtime Type Type Identification. The interface to this is simple enough: we can call typeid(x).name() on an array or pointer, x, to obtain a string representing the type of x. Note that when we compute the sizeof(a[0]) in bar()), we will get 5, because when passed to the function, a is type pointer to array of 5 characters, and the size of the type that a points to (namely, an array of 5 characters) is 5.
#include <typeinfo> #include <iostream> void foo(int a[]) { int *c; a++; // legal: type of a is int* std::cout << "sizeof int[] passed to function: " << sizeof(a) << std::endl; // same as sizeof(int*) std::cout << "typeid of int*: " << typeid(c).name() << std::endl; std::cout << "typeid of int[] passed to function: " << typeid(a).name() << std::endl; // pointer } void bar ( char a[][5] ) { char (*c)[5]; std::cout << "sizeof char a[][5] passed to function: " << sizeof(a) << std::endl; // same as sizeof(char(*)[5]) std::cout << "sizeof char a[][5] passed to function: " << sizeof(a[0]) << std::endl; // same as sizeof( char[5]) std::cout << "typeid of char(*)[5]: " << typeid(c).name() << std::endl; std::cout << "typeid of char[][5] passed to function: " << typeid(a).name() << std::endl; // pointer std::cout << "sizeof a[0]: "<< sizeof(a[0]) << std::endl; } int main() { int a[] = {2,3,4}; std::cout << "Sizeof int[3]: " << sizeof(a) << std::endl; // 3*sizeof(int) std::cout << "typeid(int[]): " << typeid(a).name() << std::endl; foo(a); std::cout << "---------------------------------" << std::endl; char b[3][5]; std::cout << "size: char b[3][5]: " << sizeof(b) << std::endl; // 3*5*sizeof(char) std::cout << "sizeof b[0]: " << sizeof(b[0]) << std::endl; // 5*sizeof(char) bar (b); }