The characteristics of pointers and arrays in C are a common source of confusion encountered on forums, IRC channels, mailing lists, tutorials, et cetera.

Aren’t arrays and pointers the same thing?

No. Consider the following:

#include <stdio.h>

int main(void) {
    char array[] = "a string literal";
    char *pointer = "another string literal";
    printf("sizeof array is %zu\n", sizeof array);
    printf("sizeof pointer is %zu\n", sizeof pointer);
    return 0;
}

// Example output on a C implementation:
// sizeof array is 17
// sizeof pointer is 8

The sizeof operator works on the types. array has type char[17] (one char for the null terminator and 16 chars for the string length) and therefore must have a size of 17. pointer has type char * so its size is implementation defined – 8 in the above example.

The fundamental difference here is that an array object has elements as part of it. A pointer consists only of a representation of something that can point to objects of that type. It is common, but not universal, that all pointers on a given C implementation are the same size. It is possible, for example, for a char * and a double * to be different sizes.

Note that you also cannot assign to an array:

int main(void) {
    char array[] = "foo";
    char *pointer = "bar";
    array = pointer; // error, can't do that to arrays.
    pointer = array; // fine, pointer would point to the
                     // first element of "foo".
    return 0;
}

If you are allowed to assign an array to a pointer but not a pointer to an array, then isn’t an array just a kind of “constant pointer”?

No. You’re not actually assigning an array to a pointer, either. In a number of contexts in C, an array is treated as a pointer to its first element. In the standard (C11 §6.3.2.1p3):

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

Since we’re attempting to assign an array to a pointer, the array is an operand of the assignment operator and therefore is converted to a pointer to its first element so we’re merely assigning a pointer to a pointer. In this case, &array[0].

In the case of the array = pointer; assignment, this is a constraint violation since the left operand of assignment operators must be a modifiable lvalue12

Arrays are not “constant pointers” or any kind of pointer. This implicit conversion exists so that you don’t have to write &array[0] all the time. The same applies in every other circumstance except the ones described in the above quote from the standard.

But I thought that void f(char x[]); and void f(char *x); are the same?

Yes, they are3! This is a special case in function parameter lists. The following are all equivalent:

void f(char *x);
void f(char x[]);
void f(char x[42]);

When you use an array like notation in a function parameter list, it is the same as declaring it as the type of a pointer to its first element. In the function call f(array), you cannot pass an array to functions, you are passing a pointer instead.

How about the fact that you can use the a[b] notation with both arrays and pointers?

Indeed. This is because a[b] is defined to be equivalent to *(a + b)4. This also uses the rule above about arrays being converted to pointers to their first element in most contexts. So when a is an operand of the additive operator in a + b it gets converted to a pointer to its first element. That pointer is then advanced b elements along the array5 and the indirection operator (unary *) yields the object that the pointer points to6. Since addition is commutative, one can also write silly things like 3[a] or 5["abcdef"].

  1. lvalues and modifiable lvalues — C11 §6.3.2.1p1

  2. Assignment operator constraints — C11 §6.5.1.6p2

  3. Function declarator parameters in array form — C11 §6.7.6.3p7

  4. a[b] equivalent to *(a + b) — C11 6.5.2.1p2

  5. Additive operator when used on pointers — C11 §6.5.6p8

  6. Unary * operator — C11 §6.5.3.2p4