Pointers and Onedimensional Arrays
Pointers and One-dimensional Arrays
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 location). 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].
Pointer Arithmetic
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 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 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.
Example 1.1: Here is a program segment to compare two
strings, character by character using pointers.
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.
Example 1.2: Here is a program segment to copy a string to another using
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.)
Program 1.2: Program to illustrate the relationship between an array and pointer
#include<stdio.h>
main()
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf("\ni=%d a[i]=%d *(a+i)=%d &a[i]=%u a+i=%u", i, a[i], *(a+i), &a[i], a+i);
}
Execute this program and observe the result
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 is 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.
Example 1.3: Here is a code fragment which copies 10
elements from array1 to array2, using pointers. It uses an end pointer,
endptr, to keep track of when it should stop copying.
int array1[10], array2[10];
int *ip1, *ip2 = &array2[0];
int *endptr = &array1[10];
for(ip1 = &array1[0]; ip1 < endptr; 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.)
Prorgam1.3: In the following program, two different pointer
variables point to the first and the last element of an integer array.
#include<stdio.h>
main()
{
int *px, *py;
int a[5]={1, 2, 3, 4, 5};
px=&a[0];
py=&a[4];
printf("px=%u py=%u", px, py);
printf("\n\n py-px=%u", py-px);
}
Execute this program and observe the result.
Similarities between Pointers and One-dimensional Arrays
There are a number of similarities between arrays and pointers in C. If
you have an array
int a[10];
you can refer to a[0], a[1], a[2], etc., or to a[i] where i is an int.
If you declare a pointer variable ip and set it to point to the
beginning of an array:
int *ip = &a[0];
you can refer to *ip, *(ip+1), *(ip+2), etc., or to *(ip+i) where i is
an int.
There are also differences, of course. You cannot assign two arrays; the
code
int a[10], b[10];
a = b; /* WRONG */
is illegal. As we've seen, though, you can assign two pointer variables:
int *ip1, *ip2;
ip1 = &a[0];
ip2 = ip1;
Pointer assignment is straightforward; the pointer on the left is simply
made to point wherever the pointer on the right does. We haven't copied
the data pointed to (there's still just one copy, in the same place);
we've just made two pointers point to that one place.
The similarities between arrays and pointers end up being quite useful,
and in fact C builds on the similarities, leading to what is called "the
equivalence of arrays and pointers in C.'' When we speak of this
"equivalence'' we do not mean that arrays and pointers are the same
thing (they are in fact quite different), but rather that they can be
used in related ways, and that certain operations may be used between
them.
The first such operation is that it is possible to (apparently) assign
an array to a pointer:
int a[10];
int *ip;
ip = a;
What can this mean? In that last assignment ip = a, C defines the result
of this assignment to be that ip receives a pointer to the first
element of a. In other words, it is as if you had written
ip = &a[0];
The second facet of the equivalence is that you can use the "array
subscripting'' notation [i] on pointers, too. If you write ip[3]
it is just as if you had written
*(ip + 3)
So when you have a pointer that points to a block of memory, such as an
array or a part of an array, you can treat that pointer "as if'' it were
an array, using the convenient [i] notation. In other words, at the
beginning of this section when we talked about *ip, *(ip+1), *(ip+2),
and *(ip+i), we could have written ip[0], ip[1], ip[2], and ip[i]. As
we'll see, this can be quite useful (or at least convenient).
The third facet of the equivalence (which is actually a more general
version of the first one we mentioned) is that whenever you mention the
name of an array in a context where the "value'' of the array would be
needed, C automatically generates a pointer to the first element of the
array, as if you had written &array[0]. When you write something
like
int a[10];
int *ip;
ip = a + 3;
it is as if you had written
ip = &a[0] + 3;
which (and you might like to convince yourself of this) gives the same
result as if you had written
ip = &a[3];
For example, if the character array
char string[100];
contains some string, here is another way to find its length:
int len;
char *p;
for(p = string; *p != '\0'; p++)
;
len = p - string;
Program 1.4: Program to swap two integers using pointers
After the loop, p points to the '\0' terminating the string. The
expression p - string is equivalent to p - &string[0], and gives the
length of the string. (Of course, we could also call strlen; in fact
here we've essentially written another implementation of strlen.)
Self Assessment Questions
i. State true or false
You can perform any type of arithmetic operation on pointers.
ii. Consider the declaration statement given below
int a[10];
What is the difference between a and &a[0]
iii. What is the control string used to display an address?
No comments:
Post a Comment