Previous: 6.1.1 Data vs Address
Up: 6.1 What is a Pointer?
Previous Page: 6.1.1 Data vs Address
Next Page: 6.2 Passing Pointers to Functions
The indirection operator, *, accesses an object of a specified type at an address. Accessing an object by its address is called indirect access. Thus, *iptr indirectly accesses the object that iptr points to, i.e. *iptr accesses x. The indirection operator is also called the contents of operator or the dereference operator. Applying the indirection operator to a pointer variable is referred to as dereferencing the pointer variable, i.e. *iptr dereferences iptr. The address of operator, &, is used to get the address of an object. We have already used it in calls to scanf(). We can also use it to assign a value to a pointer variable.
Let us consider some examples using the following declarations:
int x, z;
float y;
char ch, * pch;
int * pi, *pi2;
float * pf;
When these declarations are encountered, memory cells are allocated for these
variables at some addresses as shown in Figure 6.2.
Variables x and z are int types, is float, and ch is char. Pointer variables pi and pi2 are variables that can point to integers, is a float pointer, and pch is a character pointer. Note that the initial values of all variables, including pointer variables, are unknown. Just as we must initialize int and float variables, we must also initialize pointer variables. Here are some examples:
x = 100;
y = 20.0
z = 50;
pi = &x; /* pi points to x */
pi2 = &z; /* pi2 points to z */
pch = &ch; /* pch points to ch */
The result of executing these statements is shown in Figure 6.3: points to the cell for the variable x, points to z, pch points to ch, and pf still contains garbage. Remember, the value of a pointer variable is stored as an address in the cell; however, we do not need to be concerned with the value itself. Instead, our figure simply shows what the initialized pointer variables point to. These initialized pointers may now be used to indirectly access the objects they point to, or they be may be changed by new assignments. Here are some examples of statements and how they change things for the above memory organization. (The statements are numbered in order to reference them; the numbers are not part of the code).
1: pi2 = pi; /* pi2 points to where pi points */
/* i.e. pi2 ==> x */
2: pi = &z; /* pi now points to z, pi2 still points to x */
/* i.e. pi ==> z, pi2 ==> x */
3: *pi = *pi2; /* z = x, i.e, z = 100 */
4: *pi = 200; /* z = 200, x is unchanged */
5: *pi2 = *pi2 + 200; /* x = 300, z is unchanged */
Note, we have used a dereferenced pointer variable as the Lvalue on the left hand side of an assignment operator. The semantics is to access the object indirectly and store the value of the expression on the right hand side.
We see that the left hand side of an assignment operator, the Lvalue, can be a reference to an object either by direct access (i.e. a variable name) or by indirect access (i.e. a dereferenced pointer variable). Also notice that we were very careful about the type of the objects on the left and right hand side of the assignment operators. We have assigned an integer value to a cell pointed to by an integer pointer, and when assigning pointers, we have assigned an integer pointer to a cell declared as an int *. An assignment statement such as:
pi = x;is a legal statement in C: assigning an integer value to a pointer cell. However, the effect may not be as we would expect. The value of x will be placed in the pointer cell, pi, and subsequent dereferencing of pi, ( *pi), will use that value as a pointer (an address) to find the cell to indirectly access. This is almost never what we intend to do in this statement. Most C compilers will generate a warning at compile time stating that an illegal integer-pointer combination in an assignment was encountered to indicate that something is possibly wrong here. A warning is not an error; it does not prevent the compiler from generating a functional object file. However, it is an indication that the statement may not be what the programmer intended. Such a statement is probably correctly written as:
or
which assign a value to the cell pointed to by pi or to assign an address to pi itself, respectively. (In the RARE instance where such an assignment of an integer to a pointer cell is intended, the syntax:
pi = (int *)x;i.e. casting the integer to an integer pointer, should be used).
Likewise, an attempt to use the uninitialized variable, pf will be a disaster. Suppose we write:
printf("%f\n", *pf);
The value of pf is garbage so *pf will attempt to access the
garbage address for a float object.
The garbage value of pf may be an invalid memory address, in which case,
the program will be aborted due to a memory fault; a run time error.
This is bad news; however, we may be even more unfortunate if the value
in pf is a valid memory address.
In this case, we would access a value from some unknown place in memory.
The situation is even worse when an uninitialized pointer is used indirectly
as an Lvalue:
*pf = 3.5;Since we do not know where pf is pointing, if it happens to be a legal address, we have just placed the value, 3.5, in some unknown location in memory, possible a cell belonging to a variable in another part of the program. Finding this type of bug is very difficult. The lesson here is that care should be taken when using pointers, particularly ensuring that pointers are properly initialized.
On the other hand, the character variable, ch, is not initialized, but the pointer variable, pch is initialized to point to ch so the expression, *pch, will access the object, , correctly. If the value of *pch is accessed, it will be garbage; but a value can be stored in *pch correctly.
With proper care, the value of an initialized pointer variable (the address of some object) allows us to indirectly access the object by dereferencing the pointer variable. An example program, shown in Figure 6.9, illustrates the value of a pointer variable and the value of the object indirectly accessed by it.
Figure 6.10 shows program trace graphically. The program first declares an int and an int * variables (Figure 6.10a)). The first printf() statement prints the program title followed by the initialization of i1 and iptr (Figure 6.10b)). The next printf() gives the hexadecimal value of iptr, which is the address of i1. The next statement prints the value of the same object indirectly accessed by *iptr and directly accessed by i1. Then, the value of *iptr is changed (Figure 6.10c)); and the last statement prints the changed value of the object, accessed first indirectly and then directly.
The output for a sample run is: