Pointer Arithmetic



Item 44. Pointer Arithmetic

Pointer arithmetic is straightforward. To understand the nature of pointer arithmetic in C++, it's best to consider a pointer into an array:

const int MAX = 10;
short points[MAX];
short *curPoint = points+4;

This gives us an array and a pointer to somewhere near the middle of the array, as shown in Figure.

Effect of various arithmetic operations on the address contained in a pointer


If we increment or decrement the pointer curPoint, we are requesting that it point to the next or previous short in the points array. In other words, pointer arithmetic is always scaled in the size of the object that is being pointed to; incrementing curPoint by one does not add a one byte to the address in the pointerit adds sizeof(short) bytes. This is why there is no pointer arithmetic available on void * pointers; we don't know what type of object the void * refers to, so we can't scale the arithmetic properly.

The only time this simple scheme seems to cause confusion is in the case of multidimensional arrays, because novice C++ programmers tend to forget that a multidimensional array is an array of arrays:

const int ROWS = 2;
const int COLS = 3;
int table[ROWS][COLS]; // array of ROWS arrays of COLS ints
int (*ptable)[COLS] = table; // ptr to array of COLS ints

It's convenient to visualize the two-dimensional array shown in Figure as a table even though it's actually laid out in a linear fashion in memory, as illustrated in Figure.

A two-dimensional array is conceptually a table.


A two-dimensional array is actually a linear sequence of one-dimensional arrays.


When we perform pointer arithmetic on ptable, the arithmetic is, as always, scaled in the size of the object to which ptable points. But that object is an array of COLS ints (which is sizeof(int)*COLS bytes), not an int.

Pointers of the same type may be subtracted. The result is the number of objects (not the number of bytes) that lie between the two pointers. If the first pointer is greater (refers to a higher memory location) than the second pointer, the result is positive; otherwise it's negative. If the two pointers refer to the same object, or are both null, the result is zero. The type of the result of subtracting two pointers is the standard typedef ptrdiff_t, which is usually an alias for int. Two pointers may not be added, multiplied, or divided, because these operations just don't make conventional sense for addresses. Pointers are not integers (but see Placement New [35, 119]).

This commonly understood concept of pointer arithmetic is used as a metaphor for the design of STL iterators (see The Standard Template Library [4, 11] and Smart Pointers [42, 145]). STL iterators also permit pointer-like arithmetic that employs the same syntax as built-in pointers. In fact, built-in pointers are compliant STL iterators. Consider a possible implementation of an STL list container, as shown in Figure:

Possible implementation of a standard list. A list iterator isn't a pointer, but it is modeled on a pointer.


This configuration could have come about by executing the following code:

int a[] = { 1, 2, 3, 4 };
std::list<int> lst( a, a+3 );
std::list<int>::iterator iter = lst.begin();
++iter;

A list's iterator cannot be a built-in pointer but is instead a smart pointer with overloaded operators. The pointer arithmetic-like operation ++iter does not increment iter the way it would increment a pointer; instead, it follows a link from the current node of the list to the next. However, the analogy with arithmetic on built-in pointers is exact; the increment operation moves the iterator to the next element in the list, the way incrementing a built-in pointer moves it to the next element of an array.