Full size Banner

About pointer and references

So what is pointer? Pointer is a variable that holds an address. Oh! what is an address? An address is a memory cell where a variable is located. If we declare a variable say int i; what actually happen? Following table shows some statements and some memory locations with comments that might happen.

In the following table we see, when we declare a variable then OS assigns a memory location for it but it is upto the programmer to set the value of that variable. If a value has not been set then it contains garbage.

Statements Values Memory locations Comments Column
int i; 0xcccccccc 0x0012ff7c memory cell contains invalid value (not initialized contains garbage), programmer has not set any value 1
i=0; 0x00000000 0x0012ff7c (same location as before) Now value set to 0 2
int *ptr; 0xcccccccc 0x0012ff70 ptr is a pointer variable but not initialized, does not hold any valid address but location of ptr is at 0x0012ff70 3
ptr = &i; 0x0012ff7c 0x0012ff70 ptr had garbage value, now it has the address of variable i which is 0x0012ff70 but it's own address is unchanged(0x0012ff70) 4
*ptr = 20; 20 0x0012ff7c By this statement we tell that set value 20 to that memory location where ptr points (what address it stores, 0x0012ff7c), that means set 20 to the location 0x0012ff7c and in this location i is located. So value of i will be changed to 20 5

Now let's take a look how we can pass parameters to function to function by value and by pointer/reference. When we pass by value then a copy of the variables are passed so the contents of origianal memory locations are not changed, though we might have same variable name. see the following code snippet.

Function passed by value

int FunctionTestByValue(int a, int b)
{
cout << "a = " << a << " b = " << b << endl;
a = 1000;
b = 2000;

cout << "a = " << a << " b = " << b << endl;
return 0;
}

main()
{
int a = 200;
int b = 300;
int ret = 0;

cout << "a = " << a << " b = " << b << endl;
ret = FunctionTestByValue(a, b);

cout << "a = " << a << " b = " << b << endl;
}
Output of this program:
a = 200 b = 300
a = 200 b = 300
a = 1000 b = 2000
a = 200 b = 300

Now we see that we set the values of a and b in the function FunctionTestByValue but the when we returns to calling function values of a and b are not changed. We are passing by values that means in the callee functions a copies of variables are passed and those variables are located in the new memory places allocated by compiler. When we change anything to these new variables (means to new memory locations), this does not affect to the original memory location. So this why when we return from the function we see that changes are not reflected to caller function. Passing by value can be compared to this situation, say you have 20 Euros in your wallet. Now you give equivalent money to your friend's empty wallet so that he can use that 20 euro or he can put more money in his wallet. This will not changed money in your wallet. In passing by value is similar, the values of variables are copied to new memory location and if we change the value in new memory location that has noting to do with original memory location, as a result when the function returns these variables will be unchanged.

Function passed by pointer/reference

int FunctionTestByReference(int & a, int & b) // function prototype
{
cout << "a = " << a << " b = " << b << endl;

a = 5000;
b = 6000;

cout << "a = " << a << " b = " << b << endl;
return 0;
}

int FunctionTestByPointer(int *a, int *b)
{
cout << "*a = " << *a << " *b = " << *b << endl;
*a = 1000;
*b = 2000;
cout << "*a = " << *a << " *b = " << *b << endl;
return 0;
}

main()
{
int a = 200;
int b = 300;
int ret = 0;

cout << "a = " << a << " b = " << b << endl;
ret = FunctionTestByPointer(&a, &b);
cout << "a = " << a << " b = " << b << endl;

ret = FunctionTestByReference(a, b);
cout << "a = " << a << " b = " << b << endl;
}
Output of this program:

a = 200 b = 300
*a = 200 *b = 300
*a = 1000 *b = 2000
a = 1000 b = 2000
a = 1000 b = 2000
a = 5000 b = 6000
a = 5000 b = 6000

Now we see that we set the values of a and b in the function FunctionTestByPointer and FunctionTestByReference but the when we returns to calling function values of a and b are changed. We are passing by pointer/reference that means in the callee functions a takes address of variables. When we change some thing to these address the changes are reflected to caller program. Passing by value can be compared to this situation, say you have 20 Euros in your wallet. The situation can be compared to this, say you have 20 Euros in your wallet, now you give the wallet to your friend so that he can use the money from there or he can put more money there. After using the wallet he returns the wallet to you. Now you may have more or less than 20 Euros depending what your friend has done. This is similar to passing by pointer or reference.
constant pointer and pointer to constant data
The const keyword change how pointers are treated. The const keyword specifies that the pointer cannot be modified after initialization; the pointer is protected from modification thereafter. To declare the object pointed to by the pointer as const we use a declaration of the form:
const char *cpch;
To declare the value of the pointer that is, the actual address stored in the pointer as const we use a declaration of the form:
char * const pchc;
With this concept let's declare some variables and assign some values to it: const char cch = 'A';
char ch = 'B';
Given the preceding declarations of two objects (cch, of type const char, and ch, of type char), the following declaration/initializations are valid: const char *pch1 = &cch;
const char *const pch4 = &cch;
const char *pch5 = &ch;
char *pch6 = &ch;
char *const pch7 = &ch;
const char *const pch8 = &ch;

The following declaration/initializations are erroneous.
char *pch2 = &cch; // Error
char *const pch3 = &cch; // Error
The declaration of pch2 declares a pointer through which a constant object might be modified and is therefore disallowed. The declaration of pch3 specifies that the pointer is constant, not the object; the declaration is disallowed for the same reason the pch2 declaration is disallowed. The following eight assignments show assigning through pointer and changing of pointer value for the preceding declarations; for now, assume that the initialization was correct for pch1 through pch8.
*pch1 = 'A'; // Error: object declared const
pch1 = &ch; // OK: pointer not declared const
*pch2 = 'A'; // OK: normal pointer
pch2 = &ch; // OK: normal pointer
*pch3 = 'A'; // OK: object not declared const
pch3 = &ch; // Error: pointer declared const
*pch4 = 'A'; // Error: object declared const
pch4 = &ch; // Error: pointer declared const


References to Pointers
Now we have seen some concept how a variable can be modified by passing by pointer/reference. Many times we need to modify the pointer itself. In that case we can use similar concept. If we use pointer then we pass pointer to pointer and if we use reference then we pass by reference to pointer. Let's see this example:
The following code samples illustrate the difference between using a pointer to a pointer and a reference to a pointer. Functions Add1 and Add2 are functionally equivalent (although they are not called the same way). The difference is that Add1 uses double indirection whereas Add2 uses the convenience of a reference to a pointer.
// references_to_pointers.cpp
#include <iostream>
#include <string>
// STL namespace
using namespace std;

enum {
sizeOfBuffer = 132
};


// Define a binary tree structure.
struct BTree {
char *szText;
BTree *Left;
BTree *Right;
};


// Define a pointer to the root of the tree.
BTree *btRoot = 0;

int Add1( BTree **Root, char *szToAdd );
int Add2( BTree*& Root, char *szToAdd );
void PrintTree( BTree* btRoot );

int main( int argc, char *argv[] ) {

// Usage message
if( argc < 2 ) {
cerr << "Usage: Refptr [1 | 2]" << "\n";
cerr << "\nwhere:\n";
cerr << "1 uses double indirection\n";
cerr << "2 uses a reference to a pointer.\n";
cerr << "\nInput is from stdin.\n";
return 1;
}

char *szBuf = new char[sizeOfBuffer];
if (szBuf == NULL) {
cerr << "Out of memory!\n";
return -1;
}


// Read a text file from the standard input device and
// build a binary tree.
//while( !cin.eof() )
{
cin.get( szBuf, sizeOfBuffer, '\n' );
cin.get();

if ( strlen( szBuf ) ) {
switch ( *argv[1] ) {

// Method 1: Use double indirection.
case '1':
Add1( &btRoot, szBuf );
break;

// Method 2: Use reference to a pointer.
case '2':
Add2( btRoot, szBuf );
break;
default:
cerr << "Illegal value '"
<< *argv[1]
<< "' supplied for add method.\n"
<< "Choose 1 or 2.\n";
return -1;
}
}
}

// Display the sorted list.
PrintTree( btRoot );
}


// PrintTree: Display the binary tree in order.
void PrintTree( BTree* MybtRoot ) {
// Traverse the left branch of the tree recursively.
if ( btRoot->Left )
PrintTree( btRoot->Left );


// Print the current node.
cout << btRoot->szText << "\n";

// Traverse the right branch of the tree recursively.
if ( btRoot->Right )
PrintTree( btRoot->Right );
}


// Add1: Add a node to the binary tree.
// Uses double indirection.
int Add1( BTree **Root, char *szToAdd ) {
if ( (*Root) == 0 ) {
(*Root) = new BTree;
(*Root)->Left = 0;
(*Root)->Right = 0;
(*Root)->szText = new char[strlen( szToAdd ) + 1];
strcpy_s((*Root)->szText, (strlen( szToAdd ) + 1), szToAdd );
return 1;
}
else {
if ( strcmp( (*Root)->szText, szToAdd ) > 0 )
return Add1( &((*Root)->Left), szToAdd );
else
return Add1( &((*Root)->Right), szToAdd );
}
}


// Add2: Add a node to the binary tree.
// Uses reference to pointer
int Add2( BTree*& Root, char *szToAdd ) {
if ( Root == 0 ) {
Root = new BTree;
Root->Left = 0;
Root->Right = 0;
Root->szText = new char[strlen( szToAdd ) + 1];
strcpy_s( Root->szText, (strlen( szToAdd ) + 1), szToAdd );
return 1;
}
else {
if ( strcmp( Root->szText, szToAdd ) > 0 )
return Add2( Root->Left, szToAdd );
else
return Add2( Root->Right, szToAdd );
}
}


You can find more information from microsoft MSDN