Section 1 - Pointers, Addresses and Memory
Introduction to Week Ten
Pointers
We have dealt with our variables and their values directly up to this point. We declare a float object x, and then store the value 2.5 into it. We declare a string object name, then store "Donald" into it. There is an indirect way to deal with data in C++ and, as you can imagine, it is both trickier and more commonly used. Why programmers always select the "trickier" way to do things is a bit of a mystery. Maybe, like the alchemists of the middle ages, they want to keep their dark art secret to limit the members of the exclusive club.
You are about to gain membership into the fellowship of programmers by learning the pervasive concept of pointers in C++.
CStrings
There is another aspect of C++ that you'll have to learn if you want to understand the vast amount of C++ code already written: CStrings. These are the old-fashioned char array-based strings. They are less powerful than the string-class strings we are familiar with, but still in use. We'll look at those this week.
Reading
As usual, first study this module completely. Then, if you want more, read the (single dimensional) array material in the text.
10A.1.1 Addresses and Values
Memory is one long line of locations. Each location can hold an 8-bit item, called a byte, and the sum total of all the bytes in our computer's memory is known as our RAM, or random access memory. If your computer has 512 Meg of RAM then there are about 512 Million byte-sized memory locations (which means you probably need more memory!).
The data types that fit into a byte are the char and the unsigned char (bools may fit, too, in some implementations). Ints, floats, doubles, and the rest take up more memory. For these, we use 2, 4 or 8 byte-clumps to make a single location.
Even though we know a location is only one byte, we usually consider a double one location, and not worry about the fact that it may consist of several bytes. Using that convention, for every variable object we declare, there is exactly one memory location that it occupies, and different objects may occupy different sized memory locations.
int x; float y; unsigned char z;
x, y and z are three locations, each a different size.
Now we know what locations are. But there are really two concepts associated with each location: its address and its contents.
The address is the position (or location) of the memory location. If memory starts at location 0 and goes up to location 4,000,000,000 then an address can be any number between 0 and 4 billion. Paradoxically, even if you only have 2 Gig of memory, you might access locations much larger than 2 billion. That's because memory is numbered virtually, not physically. Think of social security numbers. Even though there are only some 40 people in this class, your social security numbers are in the millions. Still, memory has large blocks of contiguous memory, and we can think of it as being contiguous.
No matter what the data type stored in them, all addresses are the same size. It wouldn't take less breath to describe the address of a char than the address of a double:
"The char z lives at location 10,120,322"
"The double w lives at location 10,120,340"
The Content
The content of the memory location is the data item that it holds. Some locations hold a large data item, like a double, or even a user-defined object which might take hundreds of bytes, others hold a small one, like a char. In C++, we usually use the word value in place of contents.
10A.1.2 Getting the Address of a C++ Object
You have been using the symbolic names of memory objects, x, y, name, licensePlate, to manipulate them. A statement like:
licensePlate = 27;
means put a value of 27 into the contents of memory location licensePlate. That symbolic name doesn't tell us the real address of the object. But we can get it.
The ampersand operator, &, can be used to get the address of an object:
&licensePlate
This refers to the address of the variable licensePlate and can be used inside larger C++ expressions or statements. For example, to display this address at the console, you would write:
int licensePlate; cout << &licensePlate << endl; /* ----------- OUTPUT ------------- 00012FF50 --------------------------------- */
10A.1.3 Hex Notation
The number 00012FF50 is written in base 16, or hexadecimal (hex) notation.
In hex, the digits are 0-9, a-f, where a=10, b=11, ... and f=15. Each position in hex is a power of 16, rather than a power of 10 as it is in decimal.
Here are some examples:
So we can see the place in memory where the system has decided to store licensePlate. Even though, in the short program snippet immediately above, licensePlate doesn't have any value yet (or really, its value is garbage), it has a definite address.
Hex Value | Decimal Equivalent |
5 | 5 |
c | 12 |
10 | 16 |
31 | 49 |
f2 | 242 |
1000 | 4096 |
aa36f2 | 11155186 |
10A.1.4 Reference Parameters (review)
The & in front of a variable name means we are taking its address. This is closely related to another topic which also uses the & notation: reference parameters. We'll review reference parameters now for those of you who have not had them in your prior C++ experience. It is a very easy concept.
Background: When we pass arguments to methods, that data cannot be modified by the method in such a way that it changes after the method call is complete. Whatever value it had before, it still has. Here is an example where we are trying (unsuccessfully) to increase how fast Chloe wags her tail by promising her a piece of carrot:
#include <iostream> #include <string> using namespace std; // class prototypes class Dog { public: string name; int wagsPerMinute; }; // method prototypes void promisePieceOfCarrot(Dog dog); int main() { Dog chloe; // public access not usually good, but do it anyway chloe.name = "Chloe of Hood Canal"; chloe.wagsPerMinute = 30; cout << chloe.name << "'s wag frequency is " << chloe.wagsPerMinute << endl; promisePieceOfCarrot( chloe ); cout << chloe.name << "'s wag frequency is " << chloe.wagsPerMinute << endl; return 0; } // a would-be modifying method void promisePieceOfCarrot(Dog dog) { dog.wagsPerMinute *= 3; }
Here is the run:
Chloe of Hood Canal's wag frequency is 30 Chloe of Hood Canal's wag frequency is 30
Solution: If, in the formal parameter list of a method, we use the ampersand, &, in front of a parameter, we are calling this parameter a reference parameter. Technically it means we are passing an object reference rather than the object itself, to the method. But all we need to know is that it gives us a way to change the values of the parameters.
Change the function header (both in the prototype and the definition) so that it has an & in it like so:
// method prototypes void promisePieceOfCarrot(Dog &dog); // a would-be modifying method void promisePieceOfCarrot(Dog &dog) { dog.wagsPerMinute *= 3; }
Now look at the output:

Voila! This works for primitive types such as int and double as well as class object parameters.
Summary: When passing a primitive or object to a method, its value cannot be modified upon return to the client. When passing a reference to a method, the data can be modified permanently by the method.
Just remember that Chloe's wagging tail goes faster after she sees the carrot.