Pointers are one of the most powerful and confusing aspects of the C language. A pointer is a variable that holds the address of another variable. To declare a pointer, we use an asterisk between the data type and the variable name:
int *pnPtr; // a pointer to an integer value double *pdPtr; // a pointer to a double value int* pnPtr2; // also valid syntax int * pnPtr3; // also valid syntax
Note that an asterisk placed between the data type and the variable name means the variable is being declared as a pointer. In this context, the asterisk is not a multiplication. It does not matter if the asterisk is placed next to the data type, the variable name, or in the middle — different programmers prefer different styles, and one is not inherently better than the other.
Since pointers only hold addresses, when we assign a value to a pointer, the value has to be an address. To get the address of a variable, we can use the address-of operator (&):
int nValue = 5; int *pnPtr = &nValue; // assign address of nValue to pnPtr
Conceptually, you can think of the above snippet like this:

It is also easy to see using code:
int nValue = 5; int *pnPtr = &nValue; // assign address of nValue to pnPtr cout << &nValue << endl; // print the address of variable nValue cout << pnPtr << endl; // print the address that pnPtr is holding
On the author’s machine, this printed:
0012FF7C 0012FF7C
The type of the pointer has to match the type of the variable being pointed to:
int nValue = 5; double dValue = 7.0; int *pnPtr = &nValue; // ok double *pdPtr = &dValue; // ok pnPtr = &dValue; // wrong -- int pointer can not point to double value pdPtr = &nValue; // wrong -- double pointer can not point to int value
Dereferencing pointers
The other operator that is commonly used with pointers is the dereference operator (*). A dereferenced pointer evaluates to the contents of the address it is pointing to.
int nValue = 5; cout << &nValue; // prints address of nValue cout << nValue; // prints contents of nValue int *pnPtr = &nValue; // pnPtr points to nValue cout << pnPtr; // prints address held in pnPtr, which is &nValue cout << *pnPtr; // prints contents pointed to by pnPtr, which is contents of nValue
The above program prints:
0012FF7C 5 0012FF7C 5
In other words, when pnPtr is assigned to &nValue:
pnPtr is the same as &nValue
*pnPtr is the same as nValue
Because *pnPtr is the same as nValue, you can assign values to it just as if it were nValue! The following program prints 7:
int nValue = 5; int *pnPtr = &nValue; // pnPtr points to nValue *pnPtr = 7; // *pnPtr is the same as nValue, which is assigned 7 cout << nValue; // prints 7
Pointers can also be assigned and reassigned:
int nValue1 = 5; int nValue2 = 7; int *pnPtr; pnPtr = &nValue1; // pnPtr points to nValue1 cout << *pnPtr; // prints 5 pnPtr = &nValue2; // pnPtr now points to nValue2 cout << *pnPtr; // prints 7
The null pointer
Sometimes it is useful to make our pointers point to nothing. This is called a null pointer. We assign a pointer a null value by setting it to address 0:
int *pnPtr; pnPtr = 0; // assign address 0 to pnPtr
or shorthand:
int *pnPtr = 0; // assign address 0 to pnPtr
Note that in the last example, the * is not a dereference operator. It is a pointer declaration. Thus we are assigning address 0 to pnPtr, not the value 0 to the variable that pnPtr points to.
C (but not C++) also defines a special preprocessor define called NULL that evaluates to 0. Even though this is not technically part of C++, it’s usage is common enough that it will work in every C++ compiler:
int *pnPtr = NULL; // assign address 0 to pnPtr
Because null pointers point to 0, they can be used inside conditionals:
if (pnPtr)
cout << "pnPtr is pointing to an integer.";
else
cout << "pnPtr is a null pointer.";
Null pointers are mostly used with dynamic memory allocation, which we will talk about in a few lessons.
The size of pointers
The size of a pointer is dependent upon the architecture of the computer — a 32-bit computer uses 32-bit memory addresses — consequently, a pointer on a 32-bit machine is 32 bits (4 bytes). On a 64-bit machine, a pointer would be 64 bits (8 bytes). Note that this is true regardless of what is being pointed to:
char *pchValue; // chars are 1 byte
int *pnValue; // ints are usually 4 bytes
struct Something
{
int nX, nY, nZ;
};
Something *psValue; // Something is probably 12 bytes
cout << sizeof(pchValue) << endl; // prints 4
cout << sizeof(pnValue) << endl; // prints 4
cout << sizeof(psValue) << endl; // prints 4
As you can see, the size of the pointer is always the same. This is because a pointer is just a memory address, and the number of bits needed to access a memory address on a given machine is always constant.
Quiz
1) What values does this program print? Assume a short is 2 bytes, and a 32-bit machine
short nValue = 7; // &nValue = 0012FF60 short nOtherValue = 3; // &nOtherValue = 0012FF54 short *pnPtr = &nValue; cout << &nValue << endl; cout << nValue << endl; cout << pnPtr << endl; cout << *pnPtr << endl; cout << endl; *pnPtr = 9; cout << &nValue << endl; cout << nValue << endl; cout << pnPtr << endl; cout << *pnPtr << endl; cout << endl; pnPtr = &nOtherValue; cout << &nOtherValue << endl; cout << nOtherValue << endl; cout << pnPtr << endl; cout << *pnPtr << endl; cout << endl; cout << sizeof(pnPtr) << endl; cout << sizeof(*pnPtr) << endl;
Quiz solutions
6.8 — Pointers, arrays, and pointer arithmetic
|
Index
|
6.6 — C-style strings
|
6.8 — Pointers, arrays, and pointer arithmetic
Index
6.6 — C-style strings
In the code
if (pnPtr) cout < < "pnPtr is pointing to an integer."; else cout << "pnPtr is a null pointer.you forgot the second double quotation mark and a semicolon.
*gets headache*
Well, that was almost fun to read!
What exactly is the POINT of a pointer? So far I’m just thinking of pointers as duplicates of other variables with similar functionality.
You can determine the address of a value a pointer is ‘pointing’ to, but can’t you just use the ‘&’ operator to do that anyway?
I don’t understand whyyyy! T^T
Setting a pointer to point at local non-array variable is an easy way to introduce pointers, but it’s not done all that often in practice.
Pointers are used for a lot of things:
1) They are the only way you can dynamically allocate memory in C++. This is by far their most common use. This topic is covered in lesson 6.9.
2) You can use them to step through the values in an array (as an alternative to array indices).
3) You can use them to pass a large struct/class to a function in a way that doesn’t involve copying the entire struct/class, which is inefficient (covered in lesson 7.4)
4) You can use them to pass a function as a parameter to another function (covered in lesson 7.8).
5) You can use them to achieve polymorphism when dealing with inheritance (covered in lesson 12.1).
All of these things are covered in future lessons, but you’ve got to start somewhere. And this is where. :)
Example is wrong. Look at cody’s comment above.
[ How did I miss that? Wow. Thanks, it's fixed. -Alex ]