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:
1
2
3
4
5int *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 (&):
1
2int 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:
1
2
3
4
5int 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 *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.
1
2
3
4
5
6
7int 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: