Pointers and comparisons, functions, and qualifiers.1)
Relational operators can be used with pointer variables. But be aware of the difference between comparing the value of the pointer (an address) and comparing the contents of that address.
double x = 2.2; double y = 4.8; double *aPtr = &x; double *bPtr = &y; if (aPtr == bPtr) ... // compare addresses if (*aPtr == *bPtr) ... // compare contents of addresses, i.e. same as if (x == y) ...
Pointers can be used as function parameters. Arguments (i.e., actual parameters) can be anything that can be assigned to a pointer, that is, addresses or pointers.
/** Demonstrates how to pass pointers to functions. */ #include <iostream> using namespace std; void printPtr(int *); // prototype int main() { int number = 5; int *myPtr = &number; printPtr(myPtr); // pass in a pointer printPtr(&number); // pass in an address return 0; } /** * Simple function showing how to pass pointers to functions. * Print the value of nPtr and the value of dereferenced nPtr. */ void printPtr(int *nPtr) { cout << "The value of the pointer is " << nPtr << endl << "The value of the thing pointed to by the pointer is "<< *nPtr << endl; }
Pointers can be used to implement passing by reference in functions. The *
operator is used to alias a variable inside of function.
void doubleIt(int *number) { *number = 2 * (*number); }
In the function above, *number
is used as an alias for the variable passed in. Strictly speaking, the passing mechanism is pass by value because the argument that is passed into the function (an address) is copied to the formal pointer parameter number
. However, dereferencing number
acts as an alias to whatever is passed in.
/** Cube a variable using pass by reference with a pointer. */ #include <iostream> using namespace std; void cubeByReference(int *); // prototype int main() { int number = 5; cout << "The original value of number is " << number << endl; cubeByReference(&number); cout << "The new value of number is " << number << endl; return 0; } /** Cube the integer pointed to by nPtr. */ void cubeByReference(int *nPtr) { *nPtr = (*nPtr) * (*nPtr) * (*nPtr); // parenthesis for readability }
I feel pointers are much better than reference parameters for implementing pass by reference because the syntax in the function invocation (e.g., cubeByReference(&number))
makes it clear that a pointer is involved — which is good indication that pass by reference is being used.
You are already familiar with C++'s const
qualifier when used with regular variables. To review:
const
variables cannot be changed (i.e., they become constants).const
should be used on a function parameter when the function does not need to change that parameter's value.const
variable or parameter is a syntax error (which should be caught by the compiler).
Applying the const
qualifier to pointers is a little more complicated. What is constant? The pointer value or the value of the thing pointed to?
The answer is either or both.
A constant pointer is a pointer whose value cannot change; that is, constant pointers store a memory location that cannot be changed. Constant pointers must be initialized when declared, and once initialized cannot be reassigned. In other words, a constant pointer always points to the same chunk of memory.
To declare a constant pointer, use the const
keyword between the *
and the name of the pointer:
/** Attempting to modify a constant pointer to non-constant data. */ #include <iostream> using namespace std; int main() { int x = 2, y = 5; int * const myPtr = &x; // constant pointer to a (non-constant) int // The data pointed to by myPtr can be // modified through myPtr, but myPtr must // always point to the same memory location. *myPtr = 99; // change value stored in x to 99 myPtr = &y; // syntax error!! cout << *myPtr << endl; return 0; }
You can also create a pointer that is itself not constant (i.e., it can be reassigned or can change “what is being pointed to”) but that cannot change the value of “what is being pointed to.” This is a pointer to constant data or simply pointer to constant.
To create a pointer to constant data, use the const
keyword to qualify the type being pointed to.
int x = 2, y = 5; const int *myPtr = &x; // non-constant pointer to a constant int *myPtr = 99; // syntax error! myPtr = &y; // no problem, myPtr now points to y
One way to remember this is “const
modifies the thing immediately following it.”
In the case of:
int *const myPtr = &x;
const
modifies myPtr
, meaning the value of myPtr
is constant.
In the case of:
const int *myPtr = &x;
const
modifies int
, meaning the value of the int
is constant.
Combining the above, you can create a pointer that can change neither “what it is pointing to” nor the value of “what it is pointing to.”
int x = 2, y = 5; const int *const myPtr = &x;// constant pointer to a constant int *myPtr = 99; // syntax error! myPtr = &y; // syntax error!