Previous: 7.3.1 Pointers: Increment and Decrement
Up: 7.3 Arrays, Pointers, Pointer Arithmetic
Previous Page: 7.3.1 Pointers: Increment and Decrement
Next Page: 7.4 String Assignment and I/O

7.3.2 Array Names vs Pointer Variables

As we have seen, when we declare an array, a contiguous block of memory is allocated for the cells of the array and a pointer cell (of the appropriate type) is also allocated and initialized to point to the first cell of the array. This pointer cell is given the name of the array. When memory is allocated for the array cells, the starting address is fixed, i.e. it cannot be changed during program execution. Therefore, the value of the pointer cell should not be changed. To ensure that this pointer is not changed, in C, array names may not be used as variables on the left of an assignment statement, i.e. they may not be used as an Lvalue. Instead, if necessary, separate pointer variables of the appropriate type may be declared and used as Lvalues. For example, we can use pointer arithmetic and the dereference operator to initialize an array as follows:

/* Use of pointers to initialize an array */
     #include <stdio.h>
     main()
     {   int i;
         float X[MAX];

for (i = 0; i < MAX; i++) *(X + i) = 0.0; /* same as X[i] */ }

In the loop, *(X + i) is the same as X[i]. Since X (the pointer cell) has a fixed value we cannot use the increment operator or the assignment operator to change the value of X:
X = X + 1;     /* ERROR */
Here is an example of a common error which attempts to use an array as an Lvalue:
/* BUG: Attempt to use an array name as an Lvalue */
     #include <stdio.h>
     main()
     {   int i;
         float X[MAX];

for (i = 0; i < MAX; i++) { *X = 0.0; X++; /* BUG: X = X + 1; */ } }

In this example, X is fixed and cannot be used as an Lvalue; the compiler will generate an error stating that an Lvalue is required for the ++ operator. However, we can declare a pointer variable, which can point to the same type as the type of the array, and initialize it to the value of array pointer. This pointer variable CAN be used as an Lvalue, so we can then rewrite the previous array initialization loop as follows:
/*  OK: A pointer variable is initialized to an array pointer and then
         used as an Lvalue.
     */
     #include <stdio.h>
     main()
     {   int i;
         float *ptr, X[MAX];

ptr = X; /* ptr is a variable which can be assigned a value */ for (i = 0; i < MAX; i++) { *ptr = 0.0; /* *ptr accesses X[i] */ ptr++; } }

Observe that the pointer variable, ptr, is type float *, because the array is of type float. It is initialized to the value of the fixed pointer, X (i.e. the initial value of ptr is set to the same as that of X, namely, &X[0]), and may subsequently be modified in the loop to traverse the array. The first time through the loop, *ptr ( X[0]) is set to zero and ptr is incremented by one so that it points to the next element in the array. The process repeats and each element of the array is set to 0.0. This behavior is shown in Figure 7.10.

Observe that the final increment of ptr makes it point to ; however, no such element exists (recall, an array of size MAX has cells indexed 0 to MAX - 1). At the end of the for loop, the value of ptr is meaningless since it now points outside the array. Unfortunately, C does not prevent a program from accessing objects outside an array boundary; it merely increments the pointer value and accesses memory at the new address. The results of accessing the array with the pointer, ptr at this point will be meaningless and possibly disastrous. It is the responsibility of the programmer to make sure that the array boundaries are not breached. The best way of ensuring that a program stays within array boundaries is to write all loops that terminate when array limits are reached. When passing arrays in function calls, always pass the array limit as an argument as well.

Here is a similar error in handling strings and pointers:

/* BUG: Common error in accessing strings */
#include <stdio.h>
#define SIZE 100
main()
{   char c, msg[SIZE];

while ((c = getchar()) != '\n') { *msg = c; msg++; /* msg is fixed; it cannot be an Lvalue */ } *msg = '\0'; }

The array name, msg is a constant pointer; it cannot be used as an Lvalue. We can rewrite the loop correctly to read a character string as:
/* OK: Correct use of pointers to access a string */
     #include <stdio.h>
     #define SIZE 100
     main()
     {   char c, *mp, msg[SIZE];

mp = msg; while ((c = getchar()) != '\n') { *mp = c; mp++; /* mp is a variable; it can be an Lvalue */ } *mp = '\0'; }

Observe in this case, mp is a character pointer since the array is a character array. The variable, mp is initialized to the value of msg. The dereferenced pointer variable, *mp, then accesses the elements of the array in sequence as mp is incremented (see Figure 7.11). The loop terminates when a newline is read, and a terminating NULL is added to the string.

Remember, pointer variables must be initialized to point to valid objects; otherwise, fatal errors will most likely occur. For example, if the pointer, , in the above code were not initialized to the value of msg, a serious and probably fatal error will occur when the pointer is dereferenced and an attempt is made to access the memory cell pointed to by mp. This is because the initial value of mp would be some garbage value which may point to an invalid memory address causing a fatal memory fault to occur. If the garbage value were not an invalid memory address, the loop would write characters to an unknown memory address, possibly destroying other valid data.

As we've said, an array names cannot be used as an Lvalues. On the other hand, when a function is used to access an array, the corresponding formal parameter is a pointer variable. This pointer variable can be used as an Lvalue. Here is a function to print a string:

/* Function prints a string pointed to by mp. */
void our_strprint(char *mp)
{
    while (*mp) {
         putchar(*mp);
         mp++;          /* mp is a variable; it can be an Lvalue */
    }
    putchar('\n');
}
Here, is a pointer variable, which, when the function is called, we assume will be initialized to point to some NULL terminated string. The expression, *mp, accesses the elements of the array, and the loop continues as long as *mp is not NULL. Each time the loop is executed, a character, *mp, is written, and mp is incremented to point to the next character in the array. When *mp accesses the , the loop terminates and a newline character is written.



Previous: 7.3.1 Pointers: Increment and Decrement
Up: 7.3 Arrays, Pointers, Pointer Arithmetic
Previous Page: 7.3.1 Pointers: Increment and Decrement
Next Page: 7.4 String Assignment and I/O

tep@wiliki.eng.hawaii.edu
Wed Aug 17 08:56:22 HST 1994