[ fromfile: memoryaccess.xml id: pointerops ]
Following is a list of the operations that can properly be performed with pointers.
Creation – The initial value of a pointer has three possible sources:
A stack address obtained by declaring a pointer variable or a const
pointer such as an array name
An address obtained by using the address-of operator, &
A heap address obtained by a dynamic memory allocation operator (e.g., new)
Assignment
A pointer can be assigned the address stored by a pointer of the same type or of a derived type.
A variable of type void*
can be assigned a pointer of any type without an explicit cast.
A (non-void*
) pointer can be assigned the address stored by a pointer of a different (and nonderived) type only with an explicit cast.
An array name is a const
pointer and cannot be assigned to.
A NULL
pointer (value 0) can always be assigned to any pointer. (Note: Stroustrup recommends that 0 be used instead of NULL
.)
Arithmetic
Incrementing and decrementing a pointer: p++
and p--
Adding or subtracting an integer: p + k
and p - k
Such expressions are defined only if the resulting pointer value is within the range of the same array. The only exception to this rule is that a pointer is allowed to point to the memory cell that is one position beyond the end of the array as long as no attempt is made to dereference that address.
Subtracting one pointer from another: Two pointers that point to two members of an array can be subtracted yielding an int
which represents the number of array elements between the two members.
Comparison
Pointers to entries of the same array can be compared using ==
, !=
, <
, >
, etc.
Any pointer can be compared with 0.
Indirection
If p
is a pointer of type T*
, then *p
is a variable of type T
and can be used on the left side of an assignment.
A pointer p
can be used with an array index operator p[i]
where i
is an int
.
The compiler interprets such an expression as *(p+i)
.
Indexing makes sense and is defined only in the context of an array, but the compiler will not prevent its use with nonarray pointers where the results are undefined.
Example 21.7 demonstrates this last point rather clearly.
Example 21.7. src/arrays/pointerIndex.cpp
#include <iostream>
using namespace std;
int main() {
int x = 23;
int y = 45;
int* px = &x;
cout << "px[0] = " << px[0] << endl;
cout << "px[1] = " << px[1] << endl;
cout << "px[2] = " << px[2] << endl;
cout << "px[-1] = " << px[-1] << endl;
return 0;
}
Output:
// g++ on Mac OSX:
px[0] = 23
px[1] = 1606413624
px[2] = 32767
px[-1] = 45
// g++ on Linux (Ubuntu):
px[0] = 23
px[1] = -1219095387
px[2] = -1216405456
px[-1] = 45
// g++ on Windows XP (mingw)
px[0] = 23
px[1] = 45
px[2] = 2293588
px[-1] = 2009291924
// Windows XP with MS Visual Studio compiler:
px[0] = 23
px[1] = 45
px[2] = 1245112
px[-1] = 1245024
<include src="src/arrays/pointerIndex.cpp" href="src/arrays/pointerIndex.cpp" id="pointerindexcpp" mode="cpp"/>
Here we have a small, concrete example of what we mean when we talk about undefined behavior.
A beginner might be forgiven for making some assumptions about how consecutively defined variables are arranged on the program stack.
Using array subscripts on a nonarray pointer or using subscripts that are beyond the range of the array are just naughty ways of exploring that imagined landscape, right?
Well, now try to fit the subscript -1
into that intuitive map.
Then try to reconcile the different results on the different platforms.
This example is too short to show the complete picture, however.
Undefined behavior of pointers generally leads to corrupted memory – and corrupted memory is a programming nightmare – with runtime abort as one of the more desirable outcomes.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |