Previous Table of Contents Next


3.6.3. Pointer Arithmetic

C supports a rich variety of pointer arithmetic, all of which presupposes that the pointers involved point into an array of adjacent elements. Consider the fragment

int a[10];
ip = &a[3];

The expression &a[3] is a straightforward application of the & operator; it generates a pointer to the int object a[3], the fourth element of the array a. The resulting array and pointer can be represented like this:

We can now compute new pointers that also point within the array a, at offsets from the element pointed to by ip. For example, the expression ip + 1 computes a new pointer that points one element past where ip points. This new pointer can either be assigned to a second pointer variable and then used

ip2 = ip + 1;
*ip2 = 4;

or be used immediately

*(ip + 1) = 4;

In both cases, the element one past the one pointed to by ip, namely a[4], is set to 4.

As might be expected, there is nothing magic about the number 1; any integer can be added to—or subtracted from—a pointer, as long as the resulting value still points within the bounds of the underlying array. For example, ip + 2 points two elements past where ip points, and ip - 1 points one element before. Naturally, the “shorthand” forms of sections 3.3.6 and 3.3.7 work with pointers as well: ip += 1 and ip++ (among other expressions) both increment ip to point one element past where it used to, and --ip is one way to decrement ip.

After the assignment

ip2 = ip + 2;

it is obvious that ip and ip2 now point to elements separated by a distance of 2. Given just the two pointers, is it possible to recompute their separation? The answer is yes; the separation can be computed by subtracting the pointers. Pointer subtraction results in an integer that is the distance (in units of “elements”) between the two elements pointed to. So with ip2 set as shown here, ip2 - ip would give 2, and ip - ip2 would give –2.

Given that pointers can be subtracted, it is natural that they can be compared, as well. If ip and ip2 point within the same array, the <, <=, >, and >= operators all compare the relative positions of the two pointers. One pointer is greater than another if it points to a “higher” element of the array (that is, to an element with a larger subscript); two pointers are equal if they point to the same element.

Pointer addition, pointer subtraction, and the pointer comparisons that have been mentioned so far are well defined only if all pointers involved point within the same array (or single, array-like object). There is one exception: For convenience in accessing arrays via pointers, it is permissible to compute a pointer to the nonexistent element one past the end of the array, as long as this imaginary pointer is used only for comparisons. (Naturally, it would be ill defined to indirect upon this pointer to access the location pointed to because the “location” pointed to, if any, is not part of the array.) For example, here is a code fragment that sets all of the elements of an array to 0, using pointers. The pointer endp is used in the termination condition for the loop; it points one past the end of the array.

int a[10], *ip, *endp;
for(ip = &a[0], endp = &a[10]; ip < endp; ip++)
         *ip = 0;

Finally, two pointers can be compared for exact equality or inequality using the == and != operators. These comparisons have fewer restrictions: The two pointers involved do not have to point within the same object, and one or both of them can be null pointers. That is, ip == ip2 yields true if ip and ip2 point to the same object or are both null pointers, and ip != ip2 yields true if ip and ip2 point to the different objects or if one is a null pointer and one is not. A pointer equality comparison is therefore another place where it is possible to use the integer constant 0 as a null pointer constant: A condition like if(ip == 0) asks whether ip has a null pointer as its value. (Again, the preprocessor macro NULL can be used as well.)

Because the definition of a “true” boolean value in C is one that compares unequal to 0, a certain abbreviation is permissible and popular in pointer testing. If ip is a pointer, the condition

if(ip)

is, by definition, treated identically by the compiler as if the programmer had written

if(ip != 0)

That is, it asks whether ip is not a null pointer. Similarly, if(!p) asks whether p is a null pointer. We have not spoken yet of the internal values used to represent pointers (which are discussed in section 3.6.8), but it is important to note that tests like if(ip) and if(!ip) work regardless of the internal representation of null pointers.

The final pointer operation to discuss is pointer conversion. Occasionally, it is necessary to manipulate an object, via a pointer, as if it had some other type. For example, we might want to access an int value as if it were made up of individual characters, or bytes. We could initialize a char pointer, pointing at an int object, with the code

int i = 0x1234;
int *ip;
char *cp;
ip = &i;
cp = (char *)ip;

or, more simply

int i = 0x1234;
char *cp;
cp = (char *)&i;

In each case, the cast (char *) requests the conversion from type pointer-to-int to type pointer-to-char. In the cast, the type name between the parentheses, char *, takes the form of a declaration that is missing an identifier in the declarator; that is, the declarator is the lone asterisk, indicating a pointer.

Like casts in general, pointer casts are rarely needed in modern C code. One example that still requires them can be found in section 3.10.4.4. (Also, though we have not discussed pointers to functions yet, it is worth noting that pointers to data objects cannot portably be converted to function pointers, or vice versa.)


Previous Table of Contents Next