An array is a contiguous run of objects of one type. A pointer holds the address of an object or a function. The two are closely tied: most uses of an array name become a pointer to its first element, but they are different types (C17 6.2.5, 6.7.6, 6.5.6).
Arrays
direct-declarator:
direct-declarator [ [type-qualifier-list] [assignment-expression] ]
direct-declarator [ static [type-qualifier-list] assignment-expression ]
direct-declarator [ type-qualifier-list static assignment-expression ]
direct-declarator [ [type-qualifier-list] * ]
- The element type must be complete: an array of
voidor of an incomplete structure is not allowed. - A constant size must be greater than zero (C17 6.7.6.2). Zero-length arrays are a compiler extension.
- With no size, the array is incomplete. Its size may come from an initialiser, or be given by a later declaration.
- Elements are numbered from 0. The whole array’s size is
sizeof a, and the element count issizeof a / sizeof a[0]. - Arrays cannot be assigned or returned from a function, though a structure holding one can be.
- A subscript is defined as pointer arithmetic:
a[i]is*(a + i). Soi[a]means the same thing (C17 6.5.2.1).
Multidimensional arrays
An array of arrays is stored in row-major order: the elements of the last dimension are contiguous. Every dimension except the first must have a size in a parameter or other context where the type is used.
int grid[2][3] = { {1, 2, 3}, {4, 5, 6} };
grid[1][2]; /* 6 */
sizeof grid; /* 6 * sizeof(int) */
sizeof grid[0]; /* 3 * sizeof(int) */
void print(int rows, int m[][3]); /* parameter: pointer to array of 3 int */
intArray-to-pointer conversion
An expression of array type becomes a pointer to the array’s first element, except when it
is the operand of sizeof, of unary &, of
typeof and typeof_unqual in C23, or a string literal used to
initialise an array (C17 6.3.2.1). This is often called decay.
int a[10];
int *p = a; /* a decays to &a[0] */
sizeof a; /* 10 * sizeof(int) */
sizeof p; /* size of a pointer */
&a; /* pointer to the whole array: int (*)[10] */
&a[0]; /* pointer to the first element: int * */
&a and &a[0] point to the same address but have different typesAn array is not a pointer. An array declared in one file and declared as a pointer in another is a classic bug, because the second file reads the first bytes of the array as an address.
Array parameters
A parameter declared as an array is adjusted to a pointer to its first element, so the
size is ignored and sizeof gives the pointer’s size (C17 6.7.6.3). Since C99
qualifiers and static may appear in the brackets.
| Parameter | Meaning |
|---|---|
int a[] | Pointer to int |
int a[10] | Also pointer to int; the 10 has no effect |
int a[static 10] | Pointer to at least 10 valid elements, so it may not be null |
int a[const] | int *const a |
int a[*] | A variable length array of unspecified size, allowed only in a prototype |
int m[][3] | Pointer to array of 3 int |
Variable length arrays
A variable length array (VLA), added in C99, has a size computed at run time. C11 made it
optional, and __STDC_NO_VLA__ is defined when a compiler lacks it (C17 6.7.6.2).
void fill(size_t n, int v)
{
int a[n]; /* allocated at this point */
for (size_t i = 0; i < n; i++)
a[i] = v;
sizeof a; /* n * sizeof(int), evaluated at run time */
}
void process(size_t rows, size_t cols, int m[rows][cols]);
- A VLA must have automatic storage: no file scope,
staticorextern, and it cannot be a structure member. - A VLA can’t have an initialiser in C17.
- The size expression is evaluated each time the declaration is reached. It should be positive.
- The array lives until execution leaves the scope of its declaration. It is not freed when a later declaration is reached.
- A jump into the scope of a VLA from outside it is an error (C17 6.8.6.1).
- There is no way to detect an allocation failure, so a VLA with a size chosen by outside input can overflow the stack.
Pointers
A pointer type is derived from a referenced type. The & operator gives the
address of an lvalue, and the unary * gives the object a pointer refers to. See
Declarators for how the declarations
read.
int x = 1;
int *p = &x;
*p = 2; /* x is now 2 */
int **pp = &p; /* pointer to pointer */
const int *a; /* the int may not be changed through a */
int *const b = &x; /* b may not be changed */
Pointer arithmetic
Adding n to a pointer moves it by n elements, not bytes. A
pointer may refer to any element of an array or to one position past the last element.
A single object counts as an array of one element (C17 6.5.6).
| Operation | Result | Rules |
|---|---|---|
p + n, p - n | A pointer | Going outside the array, even without reading, is undefined |
q - p | ptrdiff_t: the number of elements between | Both must point into the same array or one past it |
p < q | int | Both must point into the same object, or undefined |
p == q | int | Equal if they refer to the same object or are both null. Any two pointers can be compared |
A pointer one past the end may be compared and subtracted, but dereferencing it is undefined.
int a[4] = {1, 2, 3, 4};
for (int *p = a; p < a + 4; p++)
printf("%d\n", *p);
a + 4 is valid to form, but not to read throughNull pointers
A null pointer points to no object. A null pointer constant is an
integer constant expression with value 0, or that cast to void *
(C17 6.3.2.3). The macro NULL expands to one. Dereferencing a null pointer is
undefined behaviour.
A null pointer’s bit pattern need not be all zeros, although it is on common systems.
A cast from 0, or a comparison with 0, is what produces or tests
a null pointer, whatever the bits.
C23 adds nullptr, a constant of type nullptr_t. It converts to any
pointer type and cannot be confused with an integer.
void pointers
A void * can hold a pointer to any object, and converts to and from any
object pointer type without a cast (C17 6.3.2.3). Arithmetic on void * and
dereferencing it are not allowed, although GCC permits the arithmetic as an extension. This is why
malloc needs no cast in C, unlike C++.
Function pointers
A function name decays to a pointer to the function. f, &f
and *f all give the same pointer, and a call works on any of them
(C17 6.3.2.1, 6.5.2.2).
int add(int a, int b) { return a + b; }
int (*op)(int, int) = add;
op(1, 2); /* 3 */
(*op)(1, 2); /* the same call */
typedef int binary(int, int);
binary *table[] = { add };
A pointer to a function may be converted to a pointer to another function type and back,
giving the original value, but calling through an incompatible type is undefined. C does
not define conversions between function pointers and void *. POSIX requires
that conversion to work for dlsym.
Other pointer conversions
- A pointer to an object may be converted to a pointer to a character type, in order to read its bytes.
- A pointer may be converted to an integer and an integer to a pointer. The result is
implementation-defined and need not be useful (C17 6.3.2.3).
intptr_tis the type intended for this. - A cast between object pointer types may produce a misaligned pointer. Reading through the result usually breaks the strict aliasing rule.
- A qualifier may be added implicitly, as from
int *toconst int *, but removing one requires a cast.
restrict
restrict (C99) qualifies a pointer. While the block containing the pointer’s
declaration is executing, any object that is reached through it and modified must not also be
reached in any other way (C17 6.7.3.1). The compiler can then optimise as if the objects
do not overlap.
void copy(int n, int *restrict dst, const int *restrict src)
{
for (int i = 0; i < n; i++)
dst[i] = src[i]; /* the compiler may assume dst and src do not overlap */
}
memcpy is declared with restrict; memmove, which allows overlap, is notA violation of the promise is undefined behaviour. The compiler is not required to check it.
Strings
A C string is a sequence of characters ending at the first null character. Its length is
found by reading to the null, as with strlen, which does not count the
terminator. Library routines for strings are in
<string.h>.
String literals are described on the
Lexical structure page.