====== Pointers 2 ====== Pointers and comparisons, functions, and qualifiers.((Portions loosely adapted from: Deitel, Harvey M., and Paul J. Deitel. "Pointers and Strings." In //C++: How to Program//. 3 ed. Upper Saddle River, NJ: Prentice Hall, 2001. 304-388.)) ===== Comparing pointers ===== 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 as function parameters ===== 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 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; } ===== Calling functions by reference ===== 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 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. ===== The const qualifier and pointers ===== 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. * Attempting to change a ''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//. ==== Constant pointers ==== 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 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; } ==== Pointer to constant data ==== 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. ==== Constant pointer to constant data ==== 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!