C Programming: What is the difference between an array and a pointer?
Why is a raven like a writing-desk?
(Lewis Carroll)
This is a copy of an article I wrote a long time ago. I'm putting it here to give it a more permanent home. Sorry for being off topic again!
Introduction
I'm glad you asked. The answer is surprisingly simple: almost everything. In other words, they have almost nothing in common. To understand why, we'll take a look at what they are and what operations they support.
Arrays
An array is a fixed-length collection of objects, which are stored sequentially in memory. There are only three things you can do with an array:
sizeof- get its size
You can applysizeofto it. An array x of N elements of type T (T x[N]) has the sizeN * sizeof (T), which is what you should expect. For example, ifsizeof (int) == 2andint arr[5];, thensizeof arr == 10 == 5 * 2 == 5 * sizeof (int).&- get its address
You can take its address with&, which results in a pointer to the entire array.- any other use - implicit pointer conversion
Any other use of an array results in a pointer to the first array element (the array "decays" to a pointer).
That's all. Yes, this means arrays don't provide direct access to their contents. More specifically, there is no array indexing operator.
Pointers
A pointer is a value that refers to another object (or function). You might say it contains the object's address. Here are the operations that pointers support:
sizeof- get its size
Like arrays, pointers have a size that can be obtained withsizeof. Note that different pointer types can have different sizes.&- get its address
Assuming your pointer is an lvalue, you can take its address with&. The result is a pointer to a pointer.*- dereference it
Assuming the base type of your pointer isn't an incomplete type, you can dereference it; i.e., you can follow the pointer and get the object it refers to. Incomplete types includevoidand predeclaredstructtypes that haven't been defined yet.+,-- pointer arithmetic
If you have a pointer to an array element, you can add an integer amount to it. This amount can be negative, andptr - nis equivalent toptr + -n(and-n + ptr, since+is commutative, even with pointers). Ifptris a pointer to thei'th element of an array, thenptr + nis a pointer to the(i + n)'th array element, unlessi + nis negative or greater than the number of array elements, in which case the results are undefined. Ifi + nis equal to the number of elements, the result is a pointer that must not be dereferenced.
That's it, really. However, there are a few other pointer operations defined in terms of the above fundamental operations:
->- struct dereference
p->mis equivalent to(*p).m, where.is the struct/union member access operator. This meanspmust be a pointer to a struct or union.[]- indexed dereference
a[b]is equivalent to*(a + b). This meansaandbmust be a pointer to an array element and an integer; not necessarily respectively, becausea[b] == *(a + b) == *(b + a) == b[a]. Another important equivalence isp[0] == 0[p] == *p.
A quirk of parameter declarations
However, there's one thing that confuses this issue. Whenever you declare a function parameter to have an array type, it gets silently converted to a pointer and any size information is ignored. Thus the following four declarations are equivalent:
void foo(int [42]);
void foo(int []);
typedef int t_array[23];
void foo(t_array);
void foo(int *);
A more common example is int main(int argc, char *argv[]), which is the same
as int main(int argc, char **argv). However,
int main(int argc, char argv[][]) would be an error because the above rule
isn't recursive; the result after conversion would be
int main(int argc, char (*argv)[]), i.e. argv would be a pointer to an
array of unknown size, not a pointer to a pointer.
Conclusion
Arrays by themselves are nearly useless in C. Even the fundamental []
operator, which is used for getting at the array's contents, is an illusion:
it's defined on pointers and only happens to work with arrays because of the
rule that any use of an array outside of sizeof and & yields a pointer.
I blog about Perl.
Yes, pointers in C will drive you batty. Perl's references are much easier to understand.
Note that there's an interesting parallel between C and Perl when it comes to arrays.
Neither C nor Perl have array values (that is, values that represent arrays). When you evaluate an array in C, you get a pointer to its first element. When you evaluate an array in Perl, you get a count (in scalar context) or a list of elements (in list context).
Both languages let you avoid this "decay" by taking the address of (&, C) or a reference to (\, Perl) the array.
You should put more emphasis on
sizeoffor pointers: the pointer has no idea of how many elements it points to.int array[5]; int *ptr = array; printf("%zd\n", sizeof(array)); // 5*sizeof(int) printf("%zd\n", sizeof(ptr)); // sizeof(int*) printf("%zd\n", sizeof(*ptr)); // sizeof(int)Well, that's because (with the exception of VLAs in C99)
sizeofis purely a compile time operation. It only looks at the types:sizeof EXPR == sizeof (typeof(EXPR))(if we pretend C hadtypeoffor a moment).