int* p, *q; //Pointers to two integers
int i, j;
i = 5; j = 10;
p = &i; //Obtain the address of i and save it to p
cout << p; //Prints the value of p (address of i)
cout << *p; //Prints the value of the integer that p points to (which is the value of i)
*p = j; // Overwrites the value of the location that p points to (so, i) with the value of j
*q = *p; // Overwrites the value of the location that q points to with the one that p points to
q = p; // Overwrites the pointer p with q, so they now point to the same location
A few things are worth noting here.
1. The last two commands both result in *p being equal to *q, but in different ways. In the first case, we
copy the value from one location to the other, while in the second, we make both point to the same
place.
2. The se cond -t o-last command is very risky, and will likely cause a run-time error. We have not initialized
q, so it points to an arbitrary memory location, quite possibly location 0. We are trying to overwrite it,
which most likely the program is not all owed to do. The location may b e long to the operating sys t em
or some other program
6
.
3. NULL is a special pointer we use for an uninitialized pointer variable, or to express that a pointer is not
pointing anywhere. If we try to writ e *p when p == NULL, we get a runtime error. In a sense, NULL is
really just another way of saying 0; we said above that pointers are just numbers, and the NULL pointer
points to memory location 0. In C++11, there is a new keyword for this, namely nullptr. If you set
the compiler flags to compile C++11, you can/should use nullptr instead of NULL.
4. In fact, the earlier observation about the perils of uninitialized pointers suggests that whenever you
have a pointer variable, you always assign it NULL r i ght away as you declare it. So in our example,
we should have written int *p = NULL, *q = NULL; That way, if we later forget to assign it before
dereferencing, at least, it will cause a crash of our program, rather than possibly processing some
garbage that it reads from the location. This is good coding practice to reduce your debug time.
5. & and * are inverses of each other. Thus, &*p and *&p are the same as p. (The exception is that &*p
throws a runtime error when applied to p==NULL, instead of being equal to p.)
In general, it is quite easy to mess up with pointers. A few rules of thumb (which we dis cu s s ed in c lass )
will weed out common mistakes, but generally speaking, it is easy to forget to assign a pointer correctly,
resulting in run-time errors.
We discus se d before that pointers are basically just integers. They differ in that arithmetic is somewhat
different. W h en you h ave a pointer p to an int, and you wri te p+1, the compile r assumes that what you want
is the address where the next integer will start, which is 4 bytes later. Thus, the actual add r es s r ef er en c ed
by writing p+1 is actually 4 bytes after the address of p.
This is where the type of pointer matters, as we hinted at above. When you have a void*, t he n addition
really does refer to adding individual bytes. For all others, when you wr ite p+k for a pointer p and integer
k, this references memory location p+k*size(<type>), where <type> is the type of the pointer p.
2.4 Dynamic Memory Allocation
In order to dynamically allocate and deallocate memory, there are two pairs of functions, one in C style
and one in C++ style. In C, the fun ct ion for allocating memory is malloc, and for deallocation free. In
C++, the functions are new and delete. We will first discuss the C style, as it is a little closer to the actual
low-level implementation; we’ll then see the C++ style, which shields us from s ome of the low-level details.
6
Fortunately, nowadays, all that happens is that your program throws a run-time error. In the past, the OS would often
allow you to do such an overwrite, in which case often some complete system crashes would happen.
18