C++ Pointers And Pitfalls
Chris Tralie
I'm going to summarize below some of the tricky examples we saw in class on pointers. For more fundamental ideas on pointers and dynamic memory, be sure to look at Dive Into Systems Ch.2 or watch my videos from last year on this topic.
Pointer Bug Example 1
Let's suppose our intention with the above code was change x to 174 and y to 4 and then to print their sum. We'll do it a roundabout way by setting up pointers to point to the addresses of x and y and then using these pointers. The problem with the above code is that we don't dereference the pointers to get to the variables they actually point to; we simply change p1
and p2
to the addresses 170 and 4. But these addresses probably don't point to anything that we can use in our code, and we never use them anyway.
Correct code would actually say
Remember, the code *ptr
in english says "go to the variable that the arrow ptr
points to, and substitute it into the expression." So in this case, the program ends up substituting x
in for *p1
and y
in for *p2
Pointer Bug Example 2
The issue here is that p1
has been initialized to NULL
but never updated, so it doesn't point anywhere (actually, as we discussed in class, it points to location 0, which is likely within the operating system of memory that we're never allowed to touch). Dereferencing a null pointer will always cause a segmentation fault
Pointer Bug Example 3
This code will actually print 8 instead of 174. The reason is that p1
and p2
both point to the same thing: the address of y
! Below is the picture that you want to have in your mind:
The result in the above code is that y
is first changed to 167, then immediately overwritten with 7
, but x
remains unchanged after its initialization to 1.
Passing By Reference / Stack Memory Addresses
One of the important use cases of pointers is to pass and modify variables by reference, which allows us to share variables between methods and to, in effect, return multiple things (surmounting our ordinary limitation of being able to only return a single thing from each method). Let's suppose we wrote a simple method to compute the min and max of two numbers and to store them by reference.
The minMax
method is actually able to change the values of the variables min
and max
in main
since we passed the addresses of these variables. So even though it's a void method, it changes two things that persist in main
.
Furthermore, we notice something interesting when we print the address of the variable x
in main
and its parameter copy in minMax
; namely that the address in main
is larger (note that the addresses are in hexadecimal number system rather than base 10 at the moment, and we will talk about this shortly). That's because minMax
is called from within main
, so it gets "pushed" on top of the stack in memory. But the stack actually starts at the highest address, so this means things on the top of the stack have lower addresses! (This always confused me as a student). The picture below shows this:
This also reminds us that variables get copied when we pass them along as parameters. So if we were to change the variable x
in minMax
, this would have no effect on the variable x
in main
Pointer Bug Example 4
Let's consider the code below:
This will return a segmentation fault
, because specialNumber
's stack frame will be popped off of the stack (i.e. removed) before its address is printed in main. So we're returning the address of something that's gone by the time we try to go get it, which is much like a rug pull in NFTs.