Pointer arithmetic
If all you could do with a pointer that
points at an array is treat it as if it were an alias for that array, pointers
into arrays wouldn’t be very interesting. However, pointers are more
flexible than this, since they can be modified to point somewhere else (but
remember, the array identifier cannot be modified to point somewhere
else).
Pointer arithmetic refers to the
application of some of the arithmetic operators to pointers. The reason pointer
arithmetic is a separate subject from ordinary arithmetic is that pointers must
conform to special constraints in order to make them behave properly. For
example, a common operator to use with pointers is
++, which “adds one to the pointer.”
What this actually means is that the pointer is changed to move to “the
next value,” whatever that means. Here’s an
example:
//: C03:PointerIncrement.cpp
#include <iostream>
using namespace std;
int main() {
int i[10];
double d[10];
int* ip = i;
double* dp = d;
cout << "ip = " << (long)ip << endl;
ip++;
cout << "ip = " << (long)ip << endl;
cout << "dp = " << (long)dp << endl;
dp++;
cout << "dp = " << (long)dp << endl;
} ///:~
For one run on my machine, the output
is:
ip = 6684124
ip = 6684128
dp = 6684044
dp = 6684052
What’s interesting here is that
even though the operation ++ appears to be the same operation for both
the int* and the double*, you can see that the pointer has been
changed only 4 bytes for the int* but 8 bytes for the double*. Not
coincidentally, these are the sizes of int and double on my
machine. And that’s the trick of pointer arithmetic: the compiler figures
out the right amount to change the pointer so that it’s pointing to the
next element in the array (pointer arithmetic is only meaningful within arrays).
This even works with arrays of structs:
//: C03:PointerIncrement2.cpp
#include <iostream>
using namespace std;
typedef struct {
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;
} Primitives;
int main() {
Primitives p[10];
Primitives* pp = p;
cout << "sizeof(Primitives) = "
<< sizeof(Primitives) << endl;
cout << "pp = " << (long)pp << endl;
pp++;
cout << "pp = " << (long)pp << endl;
} ///:~
The output for one run on my machine
was:
sizeof(Primitives) = 40
pp = 6683764
pp = 6683804
So you can see the compiler also does the
right thing for pointers to structs (and classes and
unions).
Pointer arithmetic also works with the
operators --, +, and
-, but the latter two operators are limited: you
cannot add two pointers, and if you subtract pointers the result is the number
of elements between the two pointers. However, you can add or subtract an
integral value and a pointer. Here’s an example demonstrating the use of
pointer arithmetic:
//: C03:PointerArithmetic.cpp
#include <iostream>
using namespace std;
#define P(EX) cout << #EX << ": " << EX << endl;
int main() {
int a[10];
for(int i = 0; i < 10; i++)
a[i] = i; // Give it index values
int* ip = a;
P(*ip);
P(*++ip);
P(*(ip + 5));
int* ip2 = ip + 5;
P(*ip2);
P(*(ip2 - 4));
P(*--ip2);
P(ip2 - ip); // Yields number of elements
} ///:~
It begins with another
macro, but this one uses a
preprocessor feature called
stringizing (implemented with the
‘#’ sign before an expression) that takes any expression and
turns it into a character array. This is quite convenient, since it allows the
expression to be printed, followed by a colon, followed by the value of the
expression. In main( ) you can see the useful shorthand that is
produced.
Although pre- and postfix versions of
++ and -- are valid with pointers, only the prefix versions are
used in this example because they are applied before the pointers are
dereferenced in the expressions above, so they allow us to see the effects of
the operations. Note that only integral values are being added and subtracted;
if two pointers were combined this way the compiler would not allow it.
Here is the output of the program
above:
*ip: 0
*++ip: 1
*(ip + 5): 6
*ip2: 6
*(ip2 - 4): 2
*--ip2: 5
In all cases, the pointer arithmetic
results in the pointer being adjusted to point to the “right place,”
based on the size of the elements being pointed to.
If pointer arithmetic seems a bit
overwhelming at first, don’t worry. Most of the time you’ll only
need to create arrays and index into them with [ ], and the most
sophisticated pointer arithmetic you’ll usually need is ++ and
--. Pointer arithmetic is generally reserved for more clever and complex
programs, and many of the containers in the Standard C++ library hide most of
these clever details so you don’t have to worry about
them.