The concepts of address and pointer are fundamental in any programming language. In C, these concepts are explicit; in some other linguagens they are hidden (and represented by the more abstract concept of reference). Learning to use pointers does not come easily; some effort and practice are required.
Table of contents:
The RAM (= random access memory) of any computer is a sequence of bytes. The place (first, second, third, etc.) of a byte in the sequence is the address of that byte. If a is the address of a byte then a + 1 is the address of the next byte.
Each variable of a program occupies a certain number of consecutive bytes in computer memory. A variable of type char occupies 1 byte. A variable of type int occupies 4 bytes and a double occupies 8 bytes in many computers. The exact number of bytes occupied by a variable is given by the sizeof operador. The value of the expression sizeof (char), for example, is 1 in all the computers and the value of the expression sizeof (int) is 4 in many computers.
Each variable (in particular, each record and each array) has an address in memory. In most computers, the address of a variable is the address of its first byte. For example, after of the declarations
char c; int i; struct { int x, y; } point; int v[4];
the variables could have the following addresses (the example is fictitious):
c 89421 i 89422 point 89426 v[0] 89434 v[1] 89438 v[2] 89442
The address of a variable is given by the & operator.
If i is a variable then
&i
is its address.
(Do not mistake this
&
for the logical and operator,
which is represented by &&
in C.)
In the example above,
&i is 89422
and
&v[3] is 89446.
For example, the second argument of the library function scanf is the address of the variable that will receive the value read from the keyboard:
int i; scanf ("%d", &i);
int main (void) { typedef struct { int day, month, year; } date; printf ("sizeof (date) = %d\n", sizeof (date)); return EXIT_SUCCESS; }
int main (void) { int i = 1234; printf (" i = %d\n", i); printf ("&i = %ld\n", (long int) &i); printf ("&i = %p\n", (void *) &i); return EXIT_SUCCESS; }
A pointer is a special kind of variable designed to store an address. A pointer can have the value
NULL
which represents an invalid
address.
The macro NULL
is defined in the stdlib.h interface
and its value is 0 (zero)
on most computers.
If a pointer p stores the address of a
variable i,
we can say
p points to i
or p is the address of i
.
(In slightly more abstracts terms, one says that p
is a reference to the variable i.)
If the value of a pointer p is not NULL
then
*p
is the value of the variable
pointed to by p.
(Do not mistake this *
for the multiplication operator!)
For example, if i is a variable and
p is &i
then *p
is the same as i
.
The following figure illustrates the idea. On the left, a pointer p, stored in the address 60001, contains the address of an integer. On the right, we see a schematic representation of the meaning of *p:
Types of pointers. There are various types of pointers: pointers to bytes, pointers to integers, pointers to pointers to integers, pointers to structs, etc. You must explicitly specify the type of pointer you want. To declare a pointer p to an integer, for example, write
int *p;
(There are those who prefer writing int* p.) To declare a pointer p to a struct recd, write
struct recd *p;
A pointer r to a pointer that will point to an integer (as in the case of a matrix of integers) is declared thus:
int **r;
Examples.
Suppose that a, b, and c
are integer variables.
Here is a silly way of doing c = a+b
:
int *p; // p is a pointer to an integer int *q; p = &a; // the value of p is the address of a q = &b; // q points to b c = *p + *q;
Another silly example:
int *p; int **r; // pointer to pointer to integer p = &a; // p points to a r = &p; // r points to p and *r points to a c = **r + b;
int main (void) { int i; int *p; i = 1234; p = &i; printf ("*p = %d\n", *p); printf (" p = %ld\n", (long int) p); printf (" p = %p\n", (void *) p); printf ("&p = %p\n", (void *) &p); return EXIT_SUCCESS; }
Suppose that we need a function that will interchange the values of two integer variables, say i and j. The function
void swap (int i, int j) { int temp; temp = i; i = j; j = temp; }
will not produce the desired effect,
since it receives the values of the variables
rather than the variables themselves.
The function receives copies
of the variables
and interchanges the values of these copies,
while the variables themselves
remain unchanged.
To obtain the desired effect,
we must provide the function with the addresses of the variables:
void swap (int *p, int *q) { int temp; temp = *p; *p = *q; *q = temp; }
To apply this function to the variables i and j just write swap (&i, &j) or, if you prefer,
int *p, *q; p = &i; q = &j; swap (p, q);
#define swap (X, Y) {int t = X; X = Y; Y = t;} . . . swap (i, j);
void swap (int *i, int *j) { int *temp; *temp = *i; *i = *j; *j = *temp; }
The elements of any array are stored in consecutive bytes of the computer memory. If each element of the array occupies s bytes, the difference between the addresses of two consecutive elements is s. But the compiler adjusts the internal details to create the illusion that the difference between the addresses of two consecutive elements is 1, regardless of the value of s. For example, after the declaration
int *v; v = malloc (100 * sizeof (int));
the address of the first element of the array is v, the address of the second element is v+1, the address of the third element is v+2, and so on. If i is a variable of type int then
v + i
is the address of the (i+1)-th element of the array. The expressions v + i and &v[i] have exactly the same value and therefore the assignments
*(v+i) = 789; v[i] = 789;
have the same effect. Hence, any of the two code fragments below can be used to fill in the array v:
for (i = 0; i < 100; ++i) scanf ("%d", &v[i]); for (i = 0; i < 100; ++i) scanf ("%d", v + i);
All these considerations are also valid for statically allocated arrays (that is, arrays allocated before the program begins to be executed) by a declaration like
int v[100];
but in this case v is a kind of
constant pointer
,
whose value cannot be changed.
void print (char *v, int n) { char *c; for (c = v; c < v + n; c++) printf ("%c", *c); }
void func1 (int x) { x = 9 * x; } void func2 (int v[]) { v[0] = 9 * v[0]; } int main (void) { int x, v[2]; x = 111; func1 (x); printf ("x: %d\n", x); v[0] = 111; func2 (v); printf ("v[0]: %d\n", v[0]); return EXIT_SUCCESS; }
The program produces the following (surprising?) output:
x: 111 v[0]: 999
Shouldn't the values of x and v[0] be equal?
orangecomes before or after
applein the dictionary. (See the section Constant character chains in the Strings chapter.) What is wrong?
char *a, *b; a = "orange"; b = "apple"; if (a < b) printf ("%s comes before %s\n", a, b); else printf ("%s comes after %s\n", a, b);
Answer: Some programmers recommend initializing all the pointers immediately by writing int *p = NULL; instead of simply int *p;. This could help to find errors in your program and could protect you against hackers. The present site does not follow this recommendation to avoid repetitive details. (Besides, it seems inelegant to assign a dummy value to a variable…)