Arrays are Not Pointers

Arrays are not the same as pointers. There are some important distinctions:

These points are illustrated with sample code. The following program illustrates the pointer arithmatic issue, and the "null array" issue. Download Code arrays_are_not_pointers.c

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. 2D static array

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;
	}


}

Two Dimensional Static Arrays versus Two-Dimensional Dynamic Pointer Arrays

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:

Two dimensional dynamic array

Passing Arrays To Functions

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.

Download Code


#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);
}