We have studied that an array is a powerful built-in data structure in the C language. It is a collection of data items of the same type stored in consecutive memory locations. An element of an array can be accessed using subscript notation, as in a [i ] , b [ i ] [ j ] , etc. Also, we can process entire arrays using loops and pass them to functions.
The power of arrays is enhanced further by having a close correspondence between arrays and pointers. The name of an array of type T is equivalent to a pointer to type T, whose value is the starting address of that array, i. e., the address of element 0. This correspondence between an array name and a pointer allows us to access array elements using pointer notation in addition to the subscript notation. We can write efficient code that runs faster using pointers for array processing.
We’ll be covering the following topics in this tutorial:
Operations with Pointers to Vector Elements
A pointer variable contains the address of the memory location to which it points, i. e., an integer value. Although a large number of operations can be performed on integer values, there are restrictions on the operations that can be performed on pointers. The various operations that can be performed on pointer variables include the following:
1. add an integer to (or subtract an integer from) a pointer 2. increment/decrement a pointer 3. subtract two pointers and 4. compare two pointers.
Note that first two operations are meaningful when pointer points to an array element and last two operations are meaningful when both pointers point to the elements of same array.
Add (Subtract) an Integer to (from) a Pointer
Consider a pointer pa that points to vector element a [ i ]. When an integer k is added to this pointer, we get a pointer that points to an element k elements past the current pointer position, i.e., pa+ k points to element a[i+k]. We can access this element, without modifying the current pointer position, using the expression * (pa+k). Similarly, pa – k points to an element k elements before the current pointer position, i.e., a [i-k].
We can also advance a pointer by k array elements by using the assignment pa+= k. However, we have to be careful while performing such operations as the resulting pointer may point to an element beyond the existing array elements, in which case, we should not access that element.
Subtraction of Pointers
Consider two pointers pa and pb that point to vector elements a [i] and a [j] , respectively,
where i < j Subtraction of these pointers, pb – pa, gives the value j – i.
Note that the array name is a pointer to the element at index 0. Thus, if pa is a pointer to element a [ i ] then the expression pa – a gives i, the index of element a [i ].
Increment and Decrement Operations with Pointers
Consider a pointer pa_ pointing to a [i], i. e., ith element of a vector a. The increment operation (++pa or pa++) causes a pointer to point to the next array element (and not the next memory byte as you might expect). Similarly, the decrement operation (–pa or pa–) causes a pointer to point to the previous array element. Actually, for a pointer of type T*, the pointer value is incremented (or decremented) by sizeof (T) bytes, which is also the size of each array element. This is true even if the pointer is positioned beyond the array limits or even if it is not pointing to an array element.
The pointer dereference and increment/decrement operations can be combined to obtain three expressions: ++*pa, *++pa and *pa++.
In expression ++*pa, the pointer is dereferenced first, thus causing the value pointed by the pointer to increment (and not the pointer itself). On the other hand, the expression *++pa increments the pointer first to point to the next array element and then dereferences it.
In expression *pa++, the pointer pa is associated first with the increment operator followed by the dereference operator (right-to-left associativity). Thus, the increment operator increments the pointer and not the value pointed by it. However, since the increment operator is used as a postfix operator, the pointer value is used first (to dereference it) and then incremented. Thus, the statement given below first assigns the value pointed by pointer pa to variable x and then increments pa.
x = *pa++;
We can also use parentheses within these pointer expressions to obtain four more expressions: ++(*pa), (*pa)++, *(pa++) and *(++pa). First note that the following pairs are equivalent: ++(*pa) and ++*pa, *(pa++) and *pa++, *(++pa) and *++pa. This is obvious as the operator within the parentheses is the one which is associated first with the pointer variable pa in absence of parentheses. However, the expression (*pa)++ is different as the pair of parentheses change the order of operator association. In this expression, the pointer variable is dereferenced first and then this value is incremented after it is used. Thus, the statement given below first assigns the value pointed by pointer pa to variable x and then increments this value.
x = (*pa)++;
Example of using increment and decrement operators with pointers
#include<stdio.h> void main() { int a[] = {1, 5, 9}; int *pa = a; /* pa points to a[0] */ int x, y, z; clrscr(); pa++; /* pa points to a[1] */ --pa; /* pa points to a[0] again */ x = ++*pa; /* a[0]=2, x=2, pa still points to a[0] */ y = *pa++; /* y=2, pa points to a[1] */ z = *++pa; /*pa points to a[2], z=pa[2] i.e. 9, a[1] unchanged */ (*pa)++; /* a[2J=10, pa still points to a[2) */ ++(*pa); /* a[2]=11, pa still points to a[2] */ pa++; /* pa points to a[3] */ printf("a[] = {%d, %d, %d}\n", a[0], a[1], a[2]); printf("x=%d y=%d, z=%d\n", x, y, z); printf("pa points to element: a[%d]\n", pa-a); getch(); }
The program output is given below
Comparison of Pointers
We can use relational operators (<, >, <=, >=) to compare two pointers provided both of them point to elements in the same array. A pointer is smaller (greater) than another pointer, if it points to an element before (after) the element pointed to by another pointer. Thus, if pointers pa and pb point to elements a [2] and a [7], respectively, the expressions pa < pb and pb > pa evaluate as true. Also, the expressions pa <= pb and pb >= pa evaluate as true.
Note that a pointer being compared may be beyond the last array element (or before the first element). However, we should not access (non-existent) array elements using such pointers. Such a pointer, beyond the last array element, is usually used as the termination criterion in loops. This will be illustrated shortly with the help an example.
We can also compare two pointers using the equality operators (==, ! =).The pointers are considered equal if they point to the same variable or array element. Note that the pointers being compared for equality need not point to the elements of the same array, as in case of relational operators. Thus, if pa and pb are pointers pointing to elements a [2] and a [7] as before and pxl and px2 are pointers pointing to integer variable x,the expressions pa == pb, pa== px1 and pxl !=px2 evaluate as false and the expressions pa != pb, pa != pxl and pxl == px2 evaluate as true.
Accessing Vector Elements by Using Array Name as a Pointer
Consider a vector a of type int declared as follows:
int a[10];
An array name a is treated as a pointer to its beginning. Thus, the value of a is the same as &a[0] and the expression a+i is a pointer to ith element. When dereferenced as * (a+i), it gives the value of the ith element, i. e., the expression * (a+i) is equivalent to a [i]. Note that the parentheses in expression * (a+i)are essential as an addition operator has lower precedence than the dereference operator. The array subscript expressions are summarized in Table along with the equivalent pointer expressions.
Table Subscript expressions and equivalent pointer expressions for a vector
Description | Using array subscript | Using pointer |
Value of the first array element | a[0] | *a |
Address of the first array element | &a[0] | a |
Value of the ith array element | a[i] | *(a+i) |
Address of the ith array element | &a[i] | a+i |
Note that when an array is declared, memory is allocated only for array elements but not for array name. Thus, an array name is not exactly identical to a pointer variable and certain operations that can be performed on pointer variables are not permitted on array names. For example, we cannot perform assignment and increment/decrement operations on array names. Thus, if a and bare int arrays and pa is an int pointer, the following operations are not permitted.
a = pa; /* wrong: cannot assign/modify address of an array */ a++; /* wrong: cannot increment/decrement an array (name) */ a = b; /* wrong: cannot assign an array to another */
Example of Vector manipulations performed by treating its name as a pointer
Consider that a and bare vectors of type int and i is a variable of type int declared as
int a[10], b[10]; int i;
Several operations on vectors performed by treating their names as pointers are given below.
a) Print an array
for (i = 0; i < n; i++) printf ("%d ", * (a+i));
This example prints the first n elements of array a. Note that the expression * (a+i) is used in place of the subscript notation a [i].
b) Read an array from the keyboard
for (i = 0; i < n; i++) scanf("%d", a+i);
This example reads first n elements of array a from the keyboard. Observe that the expression a+i, which is the address of the ith array element, is used in the scanf statement in place of &a[i].
c) Copy an array
for (i = 0; i < n; i++) * (a+i) = * (b+i);
This example copies the first n elements from array b to array a.
Accessing Vector Elements Using Another Pointer Variable
We can initialize a pointer of appropriate type so as to point to any element of a vector and then manipulate the vector elements using this pointer. Thus, if a is an integer vector and pa is an integer pointer, we can initialize pointer pa to point to the ith element of vector as
pa= &a[i];
Since the vector name is a pointer to its first element, the pointer pa can be concisely initialized to point to the first element a [0] as:
pa = a;
We can also combine pointer declaration and assignment in a single initialization statement as shown below (assuming that vector a is already declared).
int *pa = a;
Once pointer pa is initialized, it can be used to access vector elements. Thus, if pa is initialized to the first element (at index 0), the expression pa+i is the address of ith element, i.e., &a[i] and the expression= (pa+i) gives the value of the ith element, i.e., a[i].
Thus, to manipulate an entire vector using a separate pointer, first initialize a pointer to the first array element and then set up a loop to process each element using the element access expressions, * (pa+i). This technique is very similar to that used in the previous subsection in which an array name is used as a pointer. However, there is a small difference: the pointer variable can be initialized to any array element, whereas an array name always points to the beginning of the array only.
There is another more convenient and efficient method to manipulate a vector using-a pointer variable in which we make the pointer step through the vector elements. In this method, first initialize a pointer to point to the first element and then set up a loop to process each element as before. However, as the loop progresses, the pointer is made to step through the vector elements by using the pointer-increment operation. As the pointer always points to the vector element to be processed, the value of the current element is obtained by dereferencing the pointer, as *pa.
Example of Vector manipulations using pointer variables
This example illustrates the two methods mentioned above to manipulate vectors (one dimensional arrays) using pointer variables. Assume that the arrays and pointer variables are declared as follows:
int a[10], b[10], c[10]; int *pa, *pb, *pc;
a) Print an array
pa = a; for (i = 0; i < n; i++) printf ("%d ", * (pa+i));
In this example, the pointer pa is first initialized to the first element of vector a. Then a for loop is used to print the first n elements in the array. Note that the expression * (pa+i) is used in place of the subscript notation a [i ].
We can do the same thing by stepping the pointer pa to each vector element as shown below.
pa = a; for (i = 0; i < n; i++) { printf("%d ", *pa); pa++; }
Since the pointer always points to the element to be printed, the value of the ith array element is accessed by simply dereferencing the pointer as *pa. After an element is printed, the pointer variable is incremented, so as to point to the next element.
C is a very powerful language and it allows us to write very concise code. In the above code, pa++ can be combined with the element access expression *pa using the postfix increment operator, as *pa++. This eliminates one line from the body of the loop making the braces redundant. The simplified code is given below.
pa = a; for (i = 0; i < n; i++) printf("%d ", *pa++);
We can make the code more concise, by including the pointer initialization statement in the initialization expression of the for loop, using a comma operator as shown below.
for (i = 0, pa = a; i < n; i++) printf("%d ", *pa++);
Alternatively, the pointer increment can be written in the update expression of the for loop using a comma operator as shown below.
for (i = 0, pa = a; i < n; i++, pa++) printf ("%d ", *pa);
We can also eliminate the index variable i and use pointer pa as the loop control variable as shown below.
for (pa = a; pa < a + n; pa++) printf("%d ", *pa);
Note that the expression a+n is the address of the element at index n, i.e., &a[n].Alternatively, the code can also be written using a while loop as shown below.
pa = a; while (pa < a + n) printf ("%d ", *pa++);
b) read and array from the keyboard
pa = a; for (i = 0; i < n; i++) scanf("%d", pa+i);
This program segment reads the first n elements of vector a from the keyboard. Observe that the . expression pa+i,which is the address of the ith array element, is used in the scanf statement in place of &a[i ]. Alternatively, we can write this code using pointer pa to control the loop as shown below.
for (pa = a; pa < a + n; pa++) scanf ("%d", pa);
c) Add to Array
pa = a; pb = b; pc = c; for (i = 0; i < n; i++) *(pc+ i) =*(pa+ i) + *(pb + i);
This program segment first initializes pointers pa,pb and pc to point to the beginning of vectors a, b and c, respectively, and then sets up a loop to add vectors a and b to obtain vector c.
We can rewrite this code concisely in a number of ways. One such alternative is given below in which pointer pa is used as a control variable of the for loop and the pointers are incremented in element access expressions using the postfix increment operator.
for(pa =a, pb = b, pc= c; pa< a + n; ) /* update expr omitted*/ *pc++ = *pa++ + *pb++;
Although concise, the programs written in such a coding style can be difficult to read. The use of the while loop is better in such situations as it leads to more readable code, as shown below.
pa = a, pb = b, pc = c; while(pa <a+ n) *pc++ = *pa++ + *pb++;
Passing a Vector to a Function Using a Pointer
An array of type T is analogous to a pointer to type T. Thus, we can pass an array to a function using a pointer. Consider the following function prototype.
void func(int a[], int n);
The declaration of array a in this prototype can be equivalently written using a pointer as
void func(int *a, int n);
Within this function, we can access the array elements using either array notation or pointer notation. Moreover, since parameter a is a pointer variable, we can use pointer increment operations to obtain an efficient implementation of the function. Note that a call to this function is exactly same as that of the array version .
Example of Passing a vector to a function using a pointer
Consider the function given below which displays the first n elements of a vector of type int.
void ivec_print(int x[], int n) { int i; for (i = 0; i < n; i++) printf("%d ", x[i]); }
We can modify this function to use a pointer parameter by simply replacing int x [] with int =x. However, we can write a more efficient version by treating x as a pointer while manipulating the array. Such a function is given below.
void ivec_print(int *x, int n) { int i; for (i = 0; i < n; i++) printf("%d ", *x++); }
Note that parameter x is a pointer variable that points to the argument array. It is local to the function ivec_print and modifications to it’s value do not affect the argument array in the called function. (Don’t get confused here, the value of variable x is what is being discussed and not the array elements pointed by x. Of course, any change in the values of array elements pointed by parameter x will be reflected in the calling function.) Since pointer x is incremented in each iteration of the loop, it steps through the elements of the argument vector.
Further, we can eliminate the index variable i and use the pointer variable x in its place as illustrated below.
void ivec_print(int *x, int n) { int *last = x + n; for ( ; x < last; x++) printf("%d ", *x); }
Observe the use of a pointer variable last to remember the address of the element beyond the last element to be printed. It is essential as the value of pointer variable x is changing and we cannot use the expression x < x + n as a termination test.
Of course, this code would be more readable if we use a while loop as shown below.
void ivec_print(int *x, int n) { int *last = x + n; while (x < last) printf("%d ", *x++); }