Previous: 7.2 Passing Arrays to Functions
Up: 7 Arrays
Next: 7.4 String Assignment and I/O
Previous Page: 7.2 Passing Arrays to Functions
Next Page: 7.3.1 Pointers: Increment and Decrement

# 7.3 Arrays, Pointers, Pointer Arithmetic

Let us now examine how arrays are actually accessed in C. As we have seen, an array is a sequence of objects, each of the same data type. The starting address of this array of objects, i.e. the address of the first object in the array is called the base address of the array. The address of each successive element of the array is offset from the base by the size of the array type, e.g. for each successive element of an integer array, the address is offset by the size of an integer type object. As we mentioned in the previous section, in C, the name of an array used by itself in an expression evaluates to the base address of the array. That is, this value is a pointer type and points to the first object of the array. The name of the array is said to point to the array. Figure 7.7 shows an array, X[] with X pointing to (the first object of) the array. If the array is an integer array, (float array, character array, etc.) then the type of X is int * ( float *, char *, etc.). Thus, the declaration of an array causes the compiler to allocate the specified number of contiguous cells of the indicated type, as well as to allocate an appropriate pointer cell, initialized to point to the first cell of the array. This pointer cell is given the name of the array. Since X points to X[0], the following are equivalent:

X <---> &X[0]
Thus, the dereferenced pointer, *X, accesses the object, X[0], i.e. the following are equivalent:
*X <---> X[0]
As we have seen, pointer variables point to objects of a specific type. We might suspect that they can be increased or decreased to point to contiguous successive or preceding objects of the same type. In C, adding one to a pointer makes the resulting pointer point to the next object of the same type. (The value of the new pointer equals the original value of the pointer increased by the size of the object pointed to). For the array above, X + 1 points to X[1]; the increase in the pointer value is made by the appropriate size of the type involved. For example, if X is an integer array and an integer requires 4 bytes, then the value of X + 1 will be greater than that of X by 4.

Adding to a pointer results in a pointer to a successive object offset by objects from the original. Thus, points to the start of the array (the first element, X[0]), X + 1 points to the next element, X[1], and X + k points to X[k] as can be seen in Figure 7.7. Similarly, &X[k] is the same as X + k, and X[k] is the same as *(X + k). Table 7.1 summarizes pointer arithmetic and indirect access of elements of an array.

Pointer arithmetic may also involve subtraction; the resulting pointer points to a previous object offset appropriately. Thus, for example, &X[3] - 1 points to X[2], &X[5] - 3 points to X[2], and so on.

In C array access is always made through pointers and indirection operators. Whenever an expression such as X[k] appears in a program, the compiler interprets it to mean *(X + k). In other words, objects of an array are always accessed indirectly. As we have seen previously, this makes it particularly easy for a called function to indirectly access elements of an array allocated in the calling function to store or retrieve values. Let us see how function calls handle array access using the program, scores2.c of the last section. The relevant function calls in main() and the corresponding function headers are shown below for easy reference:

main()
{   int exam_scores[MAX];
...
print_intaray(exam_scores, n);
}
{
...
}
void print_intaray(int scores[], int lim)
{
...
}
When a formal parameter is declared in a function header as an array, it is interpreted as a pointer variable, NOT an array. Even if a size were specified in the formal parameter declaration, only a pointer cell is allocated for the variable, not the entire array. The type of the pointer variable is the specified type. In our example, the formal parameter, scores, is an integer pointer. It is initialized to the pointer value passed as an argument in the function call. The value passed in main() is exam_scores, a pointer to the first element of the array, exam_scores[].

Figure 7.8 illustrates the connection between the calling function, main(), and the called function, . In this case, the formal parameter, scores, is initialized to point to the value of exam_scores which is a pointer to (the first element of) the array exam_scores[]. The Figure also shows that is initialized to 10.

Within the function, read_scores(), it is now possible to access all the elements of the array, , indirectly. Since the variable, scores, in read_intaray() points to the first element of the array, exam_scores[], accesses the first element of the array, i.e. . In addition, scores + 1 points to the next element of the array, so accesses the next element, i.e. exam_scores[1]. In general, *(scores + count) accesses the element exam_scores[count]. To access elements of the array, we can either write *(scores + count) or we can write scores[count], because dereferenced array pointers and indexed array elements are identical ways of writing expressions for array access.

The functions, read_intaray() and print_intaray() can be used to read objects into any integer array and to print element values of any integer array, respectively. The calling function must simply pass, as arguments, an appropriate array pointer and maximum number of elements.

These functions may also be written explicitly in terms of indirect access, for example:

/* Function reads scores in an array. */
int read_intaray2(int * scores, int lim)
{   int n, count = 0;

printf("Type scores, EOF to quit\n"); while ((count < lim) && (scanf("%d", &n) != EOF)) { *(scores + count) = n; count++; } return count; }

Alternatively, since scores is a pointer variable, we can increment its value each time so that it points to the next object of integer type in the array, such as:
/* Function reads scores in an array. */
int read_intaray3(int * scores, int lim)
{   int n, count = 0;

printf("Type scores, EOF to quit\n"); while ((count < lim) && (scanf("%d", &n) != EOF)) { *scores = n; count++; scores++; } return count; }

The first time the loop is executed, *scores accesses the element of the array at index 0. The local pointer cell, scores, is then incremented to point to the next element of the array, at index 1. The second time the loop is executed, *scores accesses the array element at index 1. The process continues until the loop terminates.

It is also possible to mix dereferenced pointers and array indexing:

/* Function reads scores in an array. */
{   int n, count = 0;

printf("Type scores, EOF to quit\n"); while ((count < lim) && (scanf("%d", &n) != EOF)) { *(scores + count) = n; count++; } return count; }

or:
/* Function reads scores in an array. */
int read_intaray5(int * scores, int lim)
{   int n, count = 0;

printf("Type scores, EOF to quit\n"); while ((count < lim) && (scanf("%d", &n) != EOF)) { scores[count] = n; count++; } return count; }

We can also consider parts of an array, called a sub-array. A pointer to a sub-array is also an array pointer; it simply specifies the base of the sub-array. In fact, as far as C is concerned, there is no difference between an entire array and any of its sub-arrays. For example, a function call can be made to print a sub-array by specifying the starting pointer of the sub-array and its size. Suppose we wish to print the sub-array starting at exam_scores[3] containing five elements; the expression, &exam_scores[3] is a pointer to an array starting at exam_scores[3]. The function call is:

print_intaray(&exam_scores[3], 5);
Alternately, since exam_scores + 3 points to exam_scores[3], the function call can be:
print_intaray(exam_scores + 3, 5);

The passed parameters are shown visually in Figure 7.9. If either of the above function calls is used in the program, scores2.c, the values of exam_scores[3], , ..., and exam_scores[7] will be printed.

Previous: 7.2 Passing Arrays to Functions
Up: 7 Arrays
Next: 7.4 String Assignment and I/O
Previous Page: 7.2 Passing Arrays to Functions
Next Page: 7.3.1 Pointers: Increment and Decrement

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