Tuesday, February 13, 2007

Advanced Pointer Operations in C/C++

I like pointers i love pointers i use pointers so i want to explain 3 things about pointers. These are
1) Pointer to pointers
2) Pointer to functions
3) A strange syntax:)

There are much things to say. Lets begin with Pointer to pointer.

1) Pointer to pointer

Why do we need a pointer which points to a pointer? It is a common question which may be asked by a person who doesnt know much about the memory and the pointers.
Basically a pointer is a 4byte variable and nothing more. It just stores an address as a value inside it. Before explaining what a pointer to pointer is, lets examine the below code:

#include
using namespace std;

int main(){
char character = 'E';

char *ptr = &character; //pointer stores an ADDRESS ( address of character )

cout << "Address of ptr:" << &ptr << endl;
cout << "Content of ptr :"<< hex <<(int) ptr < cout << "Address of character:" << (int )&character << endl;
cout <<"Content of character:"<<*ptr <
return 0;
}

output:
Address of ptr:0x22ff68
Content of ptr :22ff6f
Address of character:22ff6f
Content of character:E
Press any key to continue . . .

we see that, ptr is a variable, it has own address and it stores the address of a variable in it. Puttin a * at the begining of a pointer provide us the value which is stored in the variable whose address is stored in pointer variable.

Most of us know about it. So why a pointer to pointer is neccessary? There may be some cases requiring some changes on pointers! For example, if we need to change a pointer's content which is passed to a function, we must use a pointer to point that pointer. Lets see the wrong example:

we write a function which takes a pointer and wants to assign its value to NULL:

void wrongChange( char* ptr ){
ptr = NULL;
}

and see the test case:

int main(){
char character = 'E';

char *ptr = &character; //pointer stores an ADDRESS ( address of character )

cout << "Content of ptr :"<< hex <<(int) ptr < wrongChange( ptr );
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}

Output is:
Content of ptr :22ff6f
Content of ptr :22ff6f
Press any key to continue . . .

You see that pointer's content doesnt change in main function. The ptr pointer in main function still points to the same address. This is because we tell it to do like this. Lets examine the wrongChange function:
1) Purpose is assigning the pointers content to NULL
2) When we call wrongChange, it allocates a variable in its stack for its local ptr.
3) After allocating a space in its stack, it copies the value of input argument which is provided by main function to its local ptr. Note that its local ptr is completly different than the ptr which is defined in main.
4) Local variable ptr is a pointer too, because we defined it as a pointer in argument list. So it is very normal to assign it a NULL value.
5) We assign a NULL value to the local ptr. However the ptr which is defined in main is still same because its address is very very different from the local ptr which is defined in stack of wrongChange function.

We see the reason why our ptr in main function is not change. Lets make some correction in our wrongChange function and rename it as correctChange :)

void correctChange( char** ptr ){
*ptr = NULL;
}

What we do in correctChange is changing the input parameter from char* to char**
Now our local ptr which is again created in stack of correctChange can point to a pointer! You can easily quess that the pointer which local ptr points will be the pointer which is given as an input paramter from main function.
A key point is that, when we call the correctChange function we shold provide a parameter which is an address of a pointer. So after we derefer the local ptr by putting an asterix, we reach the pointer which we want to change!

Our test case looks like:
int main(){
char character = 'E';

char *ptr = &character; //pointer stores an ADDRESS ( address of character )

cout << "Content of ptr :"<< hex <<(int) ptr < correctChange( &ptr );
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}

Output is:
Content of ptr :22ff6f
Content of ptr :0
Press any key to continue . . .

look at the call in main function. We give the pointer's address to correctChange by using ampersand ( i.e. &ptr ). So correctChange function uses that address to reach the pointer. Moreover if we use double * in correctChange function, we obtain the value of character variable. I mean, **ptr gives the value of 'E' in correctChange because the first asterix reaches the pointer ptr which is defined in main and the second one reaches the variable which ptr ( in main ) points to.

A major usage area of pointer to pointers is in data structers head/root pointers. For example if we want to add a node to the begining of the list, we must give the address of the head pointer to the addNode function. Because if we dont do like this, the node which we want to add, will be added to a begining of the list probably. Yes it is true, indeed it is added to the begining but the head pointer isnt updated because of using a local head pointer! We must change the head pointer for adding a node to the head of the link list. You can try and you will see ;)

2) Pointer to functions

It is also possible to point a pointer to a function. A function name is an address where the codes for that function begins. If you dont believe to me lets look at this code:

#include
using namespace std;

void function(){
cout <<"emreknlk." << endl;
}

int main(){

cout << hex << (int) function << endl;

return 0;
}

Output is:
40128a
Press any key to continue .

Now, we prove it in other ways. We can point a pointer to a function. It requires a little knowledge about how to create a pointer for pointing a function. However it is very easy. The below codes are self explanatory. Please follow the comments carefully. Begin with a simple example:

#include
using namespace std;

void function(){
cout <<"emreknlk." << endl;
}

int main(){

//first void is return type of function
//(*ptr) says that this is a pointer :)
//(void) means this function does not takes an input parameter
void (*ptr)( void );

//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = function;

//now ptr means function, they are same, they point to same address
//call the function by using ptr !!!
ptr();

return 0;
}

output is
emreknlk.


You see that, after we create a proper pointer to our function, we simply assign function to ptr and call the ptr instead of function. They both points the same address so our function is executed properly. At this example, the function has no return type and has no input parameter. However, it is also very easy for functions which takes paramters and returns a variable. Lets see an example:

#include
using namespace std;

double multiply( int num1, float num2 ){
return num1 * num2;
}

int main(){

//double is return type of function
//(*ptr) says that this is a pointer :)
//(int,float) means this function takes two input which are int and float variables
double (*ptr)( int , float );

//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = multiply;

//now ptr means multiply function, they are same, they point to same address
//call the function by using ptr !!!
cout <<"3*5.5 = " << ptr( 3, 5.5 ) << endl;

return 0;
}

output is:
3*5.5 = 16.5
Press any key to continue . . .

Usage are of pointer to functions is very large. There can be a switch mechanism for determining the function that want to be called at run time. I also compare this mechanism with the polymorphism. In polymorphism the function being called is determined in run time too by using virtual methods. You can also use pointer to functions
for fun:)


3) A strange syntax

Yesterday, i spoke with my friends and we realized that some of our friends hadnt know a syntax in C. I want to introduce it in here briefly.

Look at that code:

int main(){

char name[] = "emreknlk";

cout << name[3] << endl; //first line
cout << 3[name] << endl; //second line

cout << *(name+3) << endl; //third line
cout << *(3+name) << endl; //last line:)

return 0;
}

output:
e
e
e
e

All four line produce the same result! Look at the second line, is it a little strange? This code is perfectly correct because of the arithmetics provide the same result for four line. What compiler does is adding two constants to each other, evaluates the sum as the address of a variable and takes the variable in that address.
You know that the array name is a constant pointer which points to begining of the array. So name[3] means take the variable from name+3. In addition to this arithmetic 3[name] means take the variable from 3+name which is the same address with first.

Lets see a slightly different code:

char name[] = "emreknlk";
cout << &2[name-2] << endl;

Output is : emreknlk

This is C and this is why i like it, why i love it, why i use it:)

2 comments:

Tonguç said...

Emre sen baya bir C ci oldun bee, tebrikler cidden :)

venar303 said...

Very nice! I really liked this walkthrough, a great refresher! You added a lot of personality to a typically bland subject! thanks for your effort :)