Modern C++:Efficient and Scalable Application Development
上QQ阅读APP看书,第一时间看更新

Pointer Arithmetic

A pointer points to memory, and the type of the pointer determines the type of the data that can be accessed through the pointer. So, an int* pointer will point to an integer in memory, and you dereference the pointer (*) to get the integer. If the pointer allows it (it is not marked as const), you can change its value through pointer arithmetic. For example, you can increment or decrement a pointer. What happens to the value of the memory address depends on the type of the pointer. Since a typed pointer points to a type, any pointer arithmetic will change the pointer in units of the size of that type.

If you increment an int* pointer, it will point to the next integer in memory and the change in the memory address depends on the size of the integer. This is equivalent to array indexing, where an expression such as v[1] means you should start at the memory location of the first item in v and then move one item further in memory and return the item there:

    int v[] { 1, 2, 3, 4, 5 };
int *pv = v;
*pv = 11;
v[1] = 12;
pv[2] = 13;
*(pv + 3) = 14;

The first line allocates an array of five integers on the stack and initializes the values to the numbers 1 to 5. In this example, because an initialization list is used, the compiler will create space for the required number of items, hence the size of the array is not given. If you give the size of the array between the brackets, then the initialization list must not have more items than the array size. If the list has fewer items, then the rest of the items in the array are initialized to the default value (usually zero).

The next line in this code obtains a pointer to the first item in the array. This line is significant: an array name is treated as a pointer to the first item in the array. The following lines alter array items in various ways. The first of these (*pv) changes the first item in the array by dereferencing the pointer and assigning it a value. The second (v[1]) uses array indexing to assign a value to the second item in the array. The third (pv[2]) uses indexing, but this time with a pointer, and assigns a value to the third value in the array. And the final example (*(pv + 3)) uses pointer arithmetic to determine the address of the fourth item in the array (remember the first item has an index of 0) and then dereferences the pointer to assign the item a value. After these, the array contains the values { 11, 12, 13, 14, 5 } and the memory layout is illustrated here:

If you have a memory buffer containing values (in this example, allocated via an array) and you want to multiply each value by 3, you can do this using pointer arithmetic:

    int v[] { 1, 2, 3, 4, 5 }; 
int *pv = v;
for (int i = 0; i < 5; ++i)
{
*pv++ *= 3;
}

The loop statement is complicated, and you will need to refer back to the operator precedence given in Chapter 1Understanding Language Features. The postfix increment operator has the highest precedence, the next highest precedence is the dereference operator (*), and finally, the *= operator has the lowest of the three operators, so the operators are run in this order: ++, *, *=. The postfix operator returns the value before the increment, so although the pointer is incremented to the next item in memory, the expression uses the address before the increment. This address is then dereferenced which is assigned by the assignment operator that replaces the item with the value multiplied by 3. This illustrates an important difference between pointers and array names; you can increment a pointer but you cannot increment an array:

    pv += 1; // can do this 
v += 1; // error

You can, of course use indexing (with []) on both array names and pointers.