Thursday, July 21, 2011

Pointers a closer look


There are four things to remember when working with pointers:

1. Pointers are variables that hold memory addresses. As a program is executing, all variables are stored in memory, each at its own unique address or location. A pointer is a special type of variable that contains a memory address rather than a data value. Just as data is modified when a normal variable is used, the value of the address stored in a pointer is modified as a pointer variable is manipulated. Here's an example-
----------

int *intptr; // Declare a pointer that holds the address
// of a memory location that can store an integer. variable. intptr = new int; // Allocate memory for the inte// Note the use of * to indicate this is a pointer ger. r = 5; // Store 5 in the memory address stored in intptr. *intp t
----------

2. We usually say that a pointer "points" to the location it is storing (the "pointee"). So in the example above, intptr points to the pointee 5. 
 
Notice the use of the "new" operator to allocate memory for our integer pointee. This is something we must do before trying to access the pointee.

----------
int *ptr; // Declare integer pointer.ptr = new int; // Allocate some memory for the integer.
*ptr = 5; // Dereference to initialize the pointee.
r // to add one to the value stored // at the ptr add
*ptr = *ptr + 1; // We are dereferencing ptr in ord
eress.
----------
The* operator is used for dereferencing in C. One of the most common errors C/C++ programmers make in working with pointers is forgetting to initialize the pointee. This can sometimes cause a runtime crash because we are accessing a location in memory that contains unknown data. If we try and modify this data, we can cause subtle memory corruption making it a hard bug to track down.
Pointer assignment between two pointers makes them point to the same pointee. So the assignment y = x; makes y point to the same pointee as x. Pointer assignment does not touch the pointee. It just changes one pointer to have the same location as another pointer. After pointer assignment, the two pointers "share" the pointee. 
----------
void main() {
 int* x;  // Allocate the pointers x and y
 int* y;  // (but not the pointees).
 x = new int;  // Allocate an int pointee and set x to point to it.
 *x = 42; // Dereference x and store 42 in its pointee
 *y = 13;  // CRASH -- y does not have a pointee yet
 y = x;   // Pointer assignment sets y to point to x's pointee
 *y = 13;  // Dereference y to store 13 in its (shared) pointee
}
----------

Here is a trace of this code:
1. Allocate two pointers x and y. Allocating the pointers does not allocate any pointees.
2. Allocate a pointee and set x to point to it.
3. Dereference x to store 42 in its pointee. This is a basic     example of the dereference operation. Start at x, follow the arrow over to access its pointee.
4. Try to dereference y to store 13 in its pointee. This crashes because y does not have a pointee -- it was never assigned one
5. Assign y = x; so that y points to x's pointee. Now x and y point to the same pointee -- they are "sharing".
6. Try to dereference y to store 13 in its pointee. This time it works, because the previous assignment gave y a pointee.
As you can see, pictures are very help in understanding pointer usage. Here is another example.
----------
int my_int = 46; // Declare a normal integer variable.
/* Set it to equal 46.
 * Declare a pointer and make it point to the variable my_int
 * by using the address-of operator.
 */
int *my_pointer = &my_int;
cout << my_int << endl; // Displays 46.
*my_pointer = 107; // Derefence and modify the variable.
cout << my_int << endl; // Displays 107.
cout << *my_pointer << endl; // Also 107.
----------
Notice in this example that we never allocated memory with the   "new" operator. We declared a normal integer variable and manipulated it via pointers.

Now in this example, we illustrate the use of the delete operatorwhich de-allocates heap memory, and how we can allocate for more complex structures. Just think of the heap as a free store of    memory available to running programs.
----------
int *ptr1; // Declare a pointer to int.
ptr1 = new int; // Reserve storage and point to it.

float *ptr2 = new float; // Do it all in one statement.

delete ptr1; // Free the storage.
delete ptr2;
----------
In this final example, we show how pointers are used to pass values by reference to a function. This is how we modify the values  of variables within a function.
----------
// Passing parameters by reference.
#include <iostream>

using namespace std;

void Duplicate(int& a, int& b, int& c) {
 a *= 2;
 b *= 2;
 c *= 2;
}

int main () {
 int x = 1, y = 3, z = 7;
 Duplicate(x, y, z);
//  The following outputs: x=2, y=6, z=14.
 cout << "x="<< x << ", y="<< y << ", z="<< z;
 return 0;
}
----------
If we were to leave the &'s off the arguments in the Duplicate   function definition, we pass the variables "by value", i.e., a copyis made of the value of the variable. Any changes made to the  variable in the function modify the copy. They do not modify the original variable.
When a variable is passed by reference we are not passing a copy of its value, we are passing the address of the variable to the  function. Any modification that we do to the local variable      actually modifies the original variable passed in.
If you are a C programmer, this is a new twist. We could do the  same in C by declaring Duplicate() as Duplicate(int *x), in whichcase x is a pointer to an int, then calling Duplicate() with argument &x (address-of x), and using de-referencing of x within
Duplicate() (see below). But C++  provides a simpler way of passing values to functions by reference, even though the old "C" way of doing it still works.
----------
void Duplicate(int *a, int *b, int *c) {
 *a *= 2;
 *b *= 2;
 *c *= 2;
}

int main () {
 int x = 1, y = 3, z = 7;
 Duplicate(&x, &y, &z);
 // The following outputs: x=2, y=6, z=14.
 cout << "x=" << x << ", y=" << y << ", z=" << z;
 return 0;
}
----------

Notice with C++ references, we do not need to pass the address ofa variable, nor do we need to dereference the variable inside thecalled function.
What does the following program output? Draw a picture of memory to figure it out.
----------
void DoIt(int &foo, int goo);

int main() {
 int *foo, *goo;
 foo = new int;
 *foo = 1;
 goo = new int;
 *goo = 3;
 *foo = *goo + 3;
 foo = goo;
 *goo = 5;
 *foo = *goo + *foo;
 DoIt(*foo, *goo);
 cout << (*foo) << endl;
}

void DoIt(int &foo, int goo) {
 foo = goo + 3;
 goo = foo + 4;
 foo = goo + 3;
 goo = foo;
} 
----------
Run the program to see if you got the correct answer.


No comments:

Post a Comment