In the previous lesson, we covered the idea of a pointer: a variable which can store an address. In this short topic, we will discuss how pointers are drawn graphically, through the use of arrows (hence the name).
If you examine Program 1, there are two variables: one int and one int * (a pointer to an integer).
Program 1. Printing the value of a variable and its address.
#include <iostream> using namespace std; int main() { int n = 256; int * ptr = &n; cout << "The variable 'n' is storing " << n << endl; cout << "The address where 'n' is stored is " << ptr << endl; return 0; } |
In reality, these are two different variables, the first storing 256, the second storing the address of the first variable. If we were to draw what is actually stored in memory, it may look something like the image shown in Figure 1.
Figure 1. Two variables, one storing 256 = 0x0000010016, the other storing the address of the first.
While the image in Figure 1 may be a faithful representation of what is in the main memory of the computer, it is not intuitive. Thus, rather than drawing exactly what is stored in memory, instead, we associate the variable n with a box which stores its value (in this case, 256: we would only confuse the picture by storing 10016) and for the pointer, we simply draw an arrow pointing to the variable whose address is being stored by that pointer. This is shown in Figure 2 in two ways: the second simply implies that ptr is a pointer and is storing the address of n as opposed to explicitly using a box.
Figure 2. A simplified version of Figure 1.
If we are pointing to 0, that is, we are pointing to nothing, we represent this by having the arrow point to the empty set (∅) as is demonstrated in Figure 3.
Figure 3. A pointer to 0.
If we examine the code in Program 2, we note that ptr first stores the address of n, and then on the line marked (1), we store the address of m.
Program 2. Assigning one pointer two different values.
#include <iostream> using namespace std; int main() { int n = 256; int m = 471; int * ptr = &n; cout << "We are storing the address of 'n': " << ptr << endl; ptr = &m; // (1) cout << "We are now storing the address of 'm': " << ptr << endl; return 0; } |
Figure 4 shows the state how we could graphically represent this both before and after assignment (1).
Figure 4. The graphical representation of what occurs in Program 2.
Consider the sequence of images shown in Figure 5. Write code which which would give this sequence of changes but do not explicitly change the value of either m or n except for their initialization.
Figure 5. A sequence of pointers.
Here is one solution:
To begin, we have two variables n and m and we initialize them:
int n = 3; int m = 5;
Next, we must assign both ptr01 and ptr02 the address of n:
int * ptr01 = &n; int * ptr02 = &n;
In the next step, we change what is stored with n, so we could use:
*ptr01 = 4; // *ptr02 = 4; // this would also work
Next, we must assign ptr02 the address of m:
ptr02 = &m;
To get to the final step, we must:
There are many ways of doing this, however, here is one example:
*ptr01 = 7; *ptr02 = 8; ptr01 = ptr02;
Here is another:
*ptr01 = 7; ptr01 = &m; *ptr01 = 8;
Swap two pointers, as is shown in Figure 6 without referring to n01 or n02 after the initial assignment.
Figure 6. A graphical representation of the initial and final states of swapping two pointers.
In Program 3, we see how we can swap the two pointers, but just like swapping two actual variables, we require one additional temporary variable.
Program 3. Swapping two pointers.
#include <iostream> using namespace std; int main() { int n01 = 7; int n02 = 8; int * ptr01 = &n01; int * ptr02 = &n02; int * tmp = ptr01; // let the temporary pointer point to n01 ptr01 = ptr02; // ptr02 now points to n01 ptr02 = tmp; // ptr01 now points to n02 return 0; } |
1. Draw graphically the state of the variables in Program 3 at the points (1), (2), (3), and (4).
Program 4. Two pointers.
#include <iostream> using namespace std; int main() { int n = 256; int m = 471; int * ptr01 = &n; int * ptr02 = &m; // (1) *ptr01 = 980; // (2) ptr01 = ptr02; // (3) *ptr01 = 369; // (4) return 0; } |
2. Write code which could have caused the initial and final states shown in Figure 7, however, after the initialization provided in Program 5, you may only assign the integers 4, 5 and 6 to *ptr01. Do not refer to n01, n02, or n03 after the initialization. You may need additional temporary pointers.
Figure 7. The initial and final state of a program.
Program 5. The initializing giving the first state in Figure 7.
#include <iostream> using namespace std; int main() { int n01 = 1; int n02 = 2; int n03 = 3; int * ptr01 = &n01; int * ptr02 = &n02; int * ptr03 = &n03; // Fill in the rest... // Do not refer to n01, n02, or n03 in any way after this return 0; } |