C& CPP Tutorials
About C
Program Structure
Running C Program
Variables, constants
Operators
Control Structure
Array
Function
Pointers
Preprocessor
Structure
File
Bitwise Operators
Recursion
 Data Structures
Introduction
Stacks
Queues
Linked List
Sorting 
Searching
 Test Your Skill
Fundamentals
Input and Output
Branching and Looping
Function
Pointers I
Pointers II
Structure and Union
Sample Problems I
Sample Problems II
 Help and Support
C Forum
Source code
C  Yahoo Group
C Compilers
 

Pointers and Arrays; Pointer Arithmetic

Pointers do not have to point to single variables. They can also point at the cells of an array. For example, we can write .

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


and we would end up with ip pointing at the fourth cell of the array a (remember, arrays are 0-based, so a[0] is the first cell). We could illustrate the situation like this.


We'd use this ip just like the one in the previous section: *ip gives us what ip points to, which in this case will be the value in a[3]. 

Once we have a pointer pointing into an array, we can start doing pointer arithmetic. Given that ip is a pointer to a[3], we can add 1 to ip


 ip + 1


What does it mean to add one to a pointer? In C, it gives a pointer to the cell one farther on, which in this case is a[4]. To make this clear, let's assign this new pointer to another pointer variable.


 ip2 = ip + 1;


now  The picture now looks like this

If we now do 


 *ip2 = 4;


we've set a[4] to 4. But it's not necessary to assign a new pointer value to a pointer variable in order to use it; we could also compute a new pointer value and use it immediately.


 *(ip + 1) = 5;


In this last example, we've changed a[4] again, setting it to 5. The parentheses are needed because the unary ``contents of'' operator * has higher precedence (i.e., binds more tightly than) the addition operator. If we wrote *ip + 1, without the parentheses, we'd be fetching the value pointed to by ip, and adding 1 to that value. The expression *(ip + 1), on the other hand, accesses the value one past the one pointed to by ip.

Given that we can add 1 to a pointer, it's not surprising that we can add and subtract other numbers as well. If ip still points to a[3], then 


 *(ip + 3) = 7;

sets a[6] to 7, and 


 *(ip - 2) = 4;


sets a[1] to 4.Up above, we added 1 to ip and assigned the new pointer to ip2, but there's no reason we can't add one to a pointer, and change the same pointer.


 ip = ip + 1;


Now ip points one past where it used to (to a[4], if we hadn't changed it in the meantime). The shortcuts we learned in a previous chapter all work for pointers, too: we could also increment a pointer using 


 ip += 1;
or
 ip++;


Of course, pointers are not limited to ints. It's quite common to use pointers to other types, especially char. Here is the innards of the mystrcmp function written to use pointers. (mystrcmp, you may recall, compares two strings, character by character.) 

  char *p1 = &str1[0], *p2 = &str2[0]; 
  while(1)
   {
     if(*p1 != *p2)
     return *p1 - *p2;
     if(*p1 == '\0' || *p2 == '\0')
     return 0;
     p1++;
     p2++;
  }


The autoincrement operator ++ (like its companion, --) makes it easy to do two things at once. We've seen idioms like a[i++] which accesses a[i] and simultaneously increments i, leaving it referencing the next cell of the array a. We can do the same thing with pointers: an expression like *ip++ lets us access what ip points to, while simultaneously incrementing ip so that it points to the next element. The preincrement form works, too: *++ip increments ip, then accesses what it points to. Similarly, we can use notations like *ip-- and *--ip. 

As another example, here is the strcpy (string copy) loop from a previous chapter, rewritten to use pointers:

 char *dp = &dest[0], *sp = &src[0];
  while(*sp != '\0')
  *dp++ = *sp++;
  *dp = '\0';


(One question that comes up is whether the expression *p++ increments p or what it points to. The answer is that it increments p. To increment what p points to, you can use (*p)++.) 

When you're doing pointer arithmetic, you have to remember how big the array the pointer points into is, so that you don't ever point outside it. If the array a has 10 elements, you can't access a[50] or a[-1] or even a[10] (remember, the valid subscripts for a 10-element array run from 0 to 9). Similarly, if a has 10 elements and ip points to a[3], you can't compute or access ip + 10 or ip - 5. (There is one special case: you can, in this case, compute, but not access, a pointer to the nonexistent element just beyond the end of the array, which in this case is &a[10]. This becomes useful when you're doing pointer comparisons, which we'll look at next.) 

Pointer Subtraction and Comparison


As we've seen, you can add an integer to a pointer to get a new pointer, pointing somewhere beyond the original (as long as it's in the same array). For example, you might write 


 ip2 = ip1 + 3;

Applying a little algebra, you might wonder whether 


 ip2 - ip1 = 3


and the answer is, yes. When you subtract two pointers, as long as they point into the same array, the result is the number of elements separating them. You can also ask (again, as long as they point into the same array) whether one pointer is greater or less than another: one pointer is ``greater than'' another if it points beyond where the other one points. You can also compare pointers for equality and inequality: two pointers are equal if they point to the same variable or to the same cell in an array, and are (obviously) unequal if they don't. (When testing for equality or inequality, the two pointers do not have to point into the same array.) 

One common use of pointer comparisons is when copying arrays using pointers. Here is a code fragment which copies 10 elements from array1 to array2, using pointers. It uses an end pointer, ep, to keep track of when it should stop copying. 

   int array1[10], array2[10];
   int *ip1, *ip2 = &array2[0];
   int *ep = &array1[10];
    for(ip1 = &array1[0]; ip1 < ep; ip1++)
   *ip2++ = *ip1;


As we mentioned, there is no element array1[10], but it is legal to compute a pointer to this (nonexistent) element, as long as we only use it in pointer comparisons like this (that is, as long as we never try to fetch or store the value that it points to.) 

Null Pointers

We said that the value of a pointer variable is a pointer to some other variable. There is one other value a pointer may have: it may be set to a null pointer. A null pointer is a special pointer value that is known not to point anywhere. What this means that no other valid pointer, to any other variable or array cell or anything else, will ever compare equal to a null pointer. 

The most straightforward way to ``get'' a null pointer in your program is by using the predefined constant NULL, which is defined for you by several standard header Files, including <stdio.h>, <stdlib.h>, and <string.h>. To initialize a pointer to a null pointer, you might use code like 


  #include <stdio.h>

  int *ip = NULL;


and to test it for a null pointer before inspecting the value pointed to you might use code like 

 if(ip != NULL)
 printf("%d\n", *ip);


It is also possible to refer to the null pointer by using a constant 0, and you will see some code that sets null pointers by simply doing 


 int *ip = 0;

(In fact, NULL is a preprocessor macro which typically has the value, or replacement text, 0.) 

Furthermore, since the definition of ``true'' in C is a value that is not equal to 0, you will see code that tests for non-null pointers with abbreviated code like 


 if(ip)
  printf("%d\n", *ip);

This has the same meaning as our previous example; if(ip) is equivalent to if(ip != 0) and to if(ip != NULL). 
All of these uses are legal, and although I recommend that you use the constant NULL for clarity, you will come across the other forms, so you should be able to recognize them. 

You can use a null pointer as a placeholder to remind yourself (or, more importantly, to help your program remember) that a pointer variable does not point anywhere at the moment and that you should not use the ``contents of'' operator on it (that is, you should not try to inspect what it points to, since it doesn't point to anything). A function that returns pointer values can return a null pointer when it is unable to perform its task. (A null pointer used in this way is analogous to the EOF value that functions like getchar return.) 


  #include <stddef.h>
  char *mystrstr(char input[], char pat[])
   {
     char *start, *p1, *p2;
     for(start = &input[0]; *start != '\0'; start++)
        { /* for each position in input string... */
          p1 = pat; /* prepare to check for pattern string there */ 
          p2 = start;
          while(*p1 != '\0')
             {
               if(*p1 != *p2) /* characters differ */
               break;
               p1++;
               p2++;
            }
       if(*p1 == '\0') /* found match */
       return start;
     }
   return NULL;
  }


In general, C does not initialize pointers to null for you, and it never tests pointers to see if they are null before using them. If one of the pointers in your programs points somewhere some of the time but not all of the time, an excellent convention to use is to set it to a null pointer when it doesn't point anywhere valid, and to test to see if it's a null pointer before using it. But you must use explicit code to set it to NULL, and to test it against NULL. (In other words, just setting an unused pointer variable to NULL doesn't guarantee safety; you also have to check for the null value before using the pointer.) On the other hand, if you know that a particular pointer variable is always valid, you don't have to insert a paranoid test against NULL before using it. 

Back Next
 

Google