Pointers are references to objects (which include variables). They can be passed around as values, be stored in variables, and be arithmetically operated on. They allow one part of a program to access a variable indirectly, i.e., without knowing its name. Indeed, a variable does not need a name if you have a pointer to it.
(The term address is usually synonymous with pointer value.)
As with any other kind of value, a pointer has a
type. This type expresses
not only that the value is a pointer that can reference
an object, but also what type of object it references.
For example, a pointer that can reference an int
object
has type pointer-to-int
, which
is written as int *
.
float *
is
the type of a value that can reference a float
.
Objects can be declared of these types, and therefore
hold values of these types.
You normally obtain a pointer to a named object
obj
with the expression
&obj
, which can be read as
address of obj
.
Whatever type T
obj
is declared as, the expression
&obj
has type T *
or pointer-to-T
,
and it can be assigned to a variable of that type. For
example:
double obj; double *ptr = &obj;
ptr
is now said to point
to obj
, allowing
obj
to be accessed indirectly,
e.g., in a
place where the name obj
has no
meaning, or has a different meaning. *ptr
is now a synonym for obj
:
*ptr = 10.0; *ptr += 3.0; printf("obj's value is %g\n", *ptr);
Here, we are first assigning 10 to obj
, then incrementing it by 3, then printing
out its current value. The *
operator is said to dereference its pointer
operand, making *ptr
synonymous
with the referenced object obj
at
that moment.
A pointer variable can be made to point to different objects at different times:
double a, b, c; double *ptr; ptr = &a; *ptr = 1.0; ptr = &b; *ptr = 2.0; ptr = &c; *ptr = 3.0;
That would be equivalent to:
double a, b, c; a = 1.0; b = 2.0; c = 3.0;
For every type T, there is a
pointer-to-T type. Therefore, there is also
pointer-to-pointer-to-int
(int **
),
pointer-to-pointer-to-pointer-to-int
(int ***
),
ad nauseum. In practice, more than
two levels of indirection are extremely rare.
So far, we're not really exploiting the main utility
of pointers, which is that the same piece of code could
be made to operate on a different variable than
obj
, simply by assigning a new
value to ptr
. In practice, this is
usually done through functions with parameters of pointer
types. For example, the following idiom is used to swap
the values of two variables a
and
b
:
int a = 4, b = 9; { int tmp = a; a = b; b = tmp; }
But this piece of code is only capable of swapping the values of those two specific variables. By placing the code in a function that takes pointers to any two variables (of the right type!), we can apply it to any two such variables as often as we want:
void swap(int *p1, int *p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } int a, b, c, d; swap(&a, &b); swap(&a, &c); swap(&c, &d); swap(&b, &d);
Parameters of pointer type therefore allow us to write functions that consume information indirectly and/or produce it indirectly too. This indirection not only allows the function to assign to variables other than its own, but also to be passed (say) bulky structures with just a relatively lightweight address.
A pointer type can be made atomic by using the keyword
_Atomic
, e.g., char *_Atomic
. Such a type is
lock-free always if
ATOMIC_
is
2
, sometimes if 1
, and never if 0
.