Difference between revisions of "Template:Cpptutorial Pointers"

From assela Pathirana
Jump to navigationJump to search
m (1 revision(s))
 
(No difference)

Latest revision as of 13:08, 8 June 2007

Pointers

The concept of pointers in a simple one; however, in old C language the pointers are perhaps the single most common cause of program failure in code written by novice programmers. A good way to start, eh? Well.. pointers can be trouble makers, but the good news is, unlike in C, with C++ there many ways to avoid using them. Before we go any further, the rule of the game of pointers (at least for the 'rest of us') is don't use them unless absolutely necessary.

Having said that, using 'Pointers' for essential tasks is not difficult, and the rules that govern how they work are pretty simple. The rules can be combined to get complex results, but the individual rules remain simple.

Before we go any further, lets define what a `pointer' is.

Pointer
A pointer is a programming language data type whose value refers directly to (or “points to”) another value stored elsewhere in the computer memory using its address.

A good analogy is the relationship between a web page and its URL (address). For example http://en.wikipedia.org/wiki/Banana is URL of the page describing Bananas on Wikipedia. If we know the above address it allows us to reach the Banana Page. However, http://en.wikipedia.org/wiki/Banana is not the Banana Page, but only the address of that page.

Similarly a pointer in computer jargon is a reference (or address) of something.

Pointers and Pointees

Remember this: Addresses are pointers. The stuff referred to by those addresses (Banana Page in case of http://en.wikipedia.org/wiki/Banana or your home, in case of your postal address.) is pointee.

In C/C++ we denote this as follows:

/* Pointer/ Pointee demo */
#include<iostream>
using namespace std;
int main(){
	int *k; // the pointee of k is an integer. 
	int y;  // y is an integer
	y=0;    // set y to zero
	k=&y;   // point k to the address of y (y is pointee of k now)
	*k=40;  // set the value of pointee of k (ah, ha!) to 40. 
	cout << y <<"\n"; // print y. 
	cout << (*k) <<"\n"; // print pointee of k
}

The notation * can be read as pointee of and & as address of.

What k=&y; *k=40; does is equivalent of y=40 in a very round about way!! This code shows one of the major issues of pointer usage. Where there are pointers -- there are hidden links. If we don't keep a track of these links, we are inviting for trouble!

If you are interested in a more in-depth coverage of the pointers in general make a detour to this link. The rest of this article covers only two important aspects of pointers.

Using Pointers to get values out of functions

The most straight forward way to get the results of a function is its return value. If you need to get several values out of a function how do you do that? One way is to use Data Structures, a topic we are yet to cover. Another is to use pointers. See the following example:

#include <iostream>
using namespace std;
void add(int *a){ // pointee of a is an integer
	(*a)+=5;		  // add 5 to pointee of a
}

int main ()
{
	int val=0;      // val is zero
        int *k;  // k's pointee is an integer. 
	k=&val;  //now k points to the address of val
	add(k);      // pass k, i.e. address of val
	cout << val;    // val has changed! 
}

The following section is a formal explanation of what is happening.

Passing by value or by reference

All the simple functions we have seen in section Functions and thereafter, the arguments passed to the functions have been passed by value. This means that when calling a function with parameters, what we have passed to the function were copies of their values but never the variables themselves. For example in the followng code

// function example
#include <iostream>
using namespace std;


int addition (int a, int b)
{
  int r;
  r=a+b;
  return (r);
}

int main ()
{
  int z;
  z = addition (5,3);
  cout << "The result is " << z;
  return 0;
}

What we did in this case was to call to function addition passing the values of x and y, i.e. 5 and 3 respectively, but not the variables x and y themselves.

File:8-imgfunc1.gif

This way, when the function addition is called, the value of its local variables a and b become 5 and 3 respectively, but any modification to either a or b within the function addition will not have any effect in the values of x and y outside it, because variables x and y were not themselves passed to the function, but only copies of their values at the moment the function was called.

But there might be some cases where you need to manipulate from inside a function the value of an external variable. For that purpose we can use arguments passed by reference, as in the function duplicate of the following example:

// 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);
  cout << "x=" << x << ", y=" << y << ", z=" << z;
  return 0;
}

x=2, y=6, z=14

The first thing that should call your attention is that in the declaration of duplicate the type of each parameter was followed by an ampersand sign (&). This ampersand is what specifies that their corresponding arguments are to be passed by reference instead of by value.

When a variable is passed by reference we are not passing a copy of its value, but we are somehow passing the variable itself to the function and any modification that we do to the local variables will have an effect in their counterpart variables passed as arguments in the call to the function.

File:8-imgfunc3.gif

To explain it in another way, we associate a, b and c with the arguments passed on the function call (x, y and z) and any change that we do on a within the function will affect the value of x outside it. Any change that we do on b will affect y, and the same with c and z.

That is why our program's output, that shows the values stored in x, y and z after the call to duplicate, shows the values of all the three variables of main doubled.

If when declaring the following function:

void duplicate (int& a, int& b, int& c)

we had declared it this way:

void duplicate (int a, int b, int c)

i.e., without the ampersand signs (&), we would have not passed the variables by reference, but a copy of their values instead, and therefore, the output on screen of our program would have been the values of x, y and z without having been modified.

Passing by reference is also an effective way to allow a function to return more than one value. For example, here is a function that returns the previous and next numbers of the first parameter passed.

// more than one returning value

#include <iostream>
using namespace std;

void prevnext (int x, int& prev, int& next)
{
  prev = x-1;
  next = x+1;
}


int main ()
{
  int x=100, y, z;
  prevnext (x, y, z);
  cout << "Previous=" << y << ", Next=" << z;
  return 0;
}

Previous=99, Next=101

Passing functions as pointers

Note
This section can be skipped without much harm!

We can pass whole functions as arguments to other functions using pointers. See the following example.

#include <iostream>
using namespace std;
int oper(int one, int two, int (*myfunc)(int,int)){ // pointee of myfunc is a function 
						    //and takes two integer arguments
						    // and return an integer. 
	return (*myfunc)(one,two);		    // &x - 'pointer of x is..'
						    // *x - 'pointee of x is..'
										
}

int bigger(int a, int b){
	if(a>b){
		return a;
	}else{
		return b;
	}
}

int smaller(int a, int b){
	if(a>b){
		return b;
	}else{
		return a;
	}
}

int main ()
{
	int a=5, b=10;
	cout << oper(a,b,&bigger)<<'\n';
	cout << oper(a,b,&smaller)<<'\n';
}