Wednesday 30 March 2016

Pointer Concepts in C/C++

Pointers, a very apt name considering they sting a bit or a lot depending on how many of them are there. 
I am assuming that the reader is familiar with pointers to the extent of simply understanding what they mean. From here in out we'll be looking into different ways and places where pointers can be used and how they are to be used. After all we don't want any injuries, do we?
Let's get on with it then, the basic syntax for defining a pointer is as follows:
type *name;
Where type is what we call as the base type. The base type of the pointer defines what type of variable the pointer can point to.
Technically, any type of pointer can point anywhere in memory. However that can be problematic when it comes to pointer arithmetic. You see pointer arithmetic is done relative to the base type, so it is important to declare the pointer correctly. 


Making pointers point to variables of types different then the base type of the pointer:

As I said before, a pointer cares about holding an address, and all addresses in memory are the same to it. It doesn't really care what data type is stored at the address. The base type only restricts the number of memory addresses one pointer can refer to at once. So if we have a pointer of type int, then that only means that this pointer will point to 4 memory addresses at once (4 being the number of memory addresses required by an int). So to this pointer, we give an address and it automatically assumes that this address and the consecutive three addresses after that, are the ones it is supposed to point to. All we have to do is give pointer a starting address and then some how make sure that the pointer is not fed with more addresses then it can handle by definition. Hence we can make an int pointer point to a double variable by using type casting (making sure the pointer gets exactly what it can handle):
double x;
int *p;
p=(int *)&x; //p=(int sized)starting address of something.

Multiple Indirections (pointers to pointers):

It's like keeping a key for a lock that contains another key which in turn opens the lock where the actual cash is. But you probably knew that already. Following is the syntax:
type **name;
The double asterisk is not only necessary while declaring a pointer to a pointer but is also necessary when extracting values using these doubly indirected pointers.


int x, *p, **q; //x is an integer. p is a pointer to an integer. q is a pointer to a pointer to an integer.

x=10;
p=&x;
q=&p;
Now accessing x can be done using any of the following three ways:
x //equals 10
*p //equals 10
**q //equals 10


Initializing Pointers:

Pointers are supposed to carry addresses and they will carry addresses. Now whether these addresses mean anything or not is another issue. You can either give an address to a pointer (that's what you should do) or you can let the compiler give the pointer some meaningless value (to keep it worm). Global and static local pointers are automatically initialized to null, other pointers are given unknown, random values. Of course this assignment of NULL value can be done by you as well:
type *p=NULL or 0;

Pointer Arithmetic:
Pointer arithmetic is what distinguishes C/C++ from most languages like Java and Python. These languages only go as far as dealing with objects and variables by passing their addresses around. C/C++ goes the extra mile. And mind you it's a very long mile (wait, that can't be right, a mile is a mile, there is no such thing as a long mile, all miles are the same length). Anyway, pointer arithmetic is like a luxury meaning that you can live without it but if you have a good handle over it then that means you deserve some intense reverence. 
As the name suggests, pointer arithmetic means adding and subtracting pointers together to create magic. I find it important from a reader's perspective. If I am given the task of reading and then figuring out some one's else's code in C, then that most likely means that code has the worth of being considered by someone other than the creator. And if that code has such worth then it most likely is filled with a lot of pointer arithmetic. 
Let their be a pointer p of data type int, then we are allowed to perform only addition and subtraction operations on it and those too are governed by the following rule:
p can be added to or subtracted from anything which is a multiple of the size of int (the base type).
We can increment p, decrement p, add some number to p, subtract sum number from p.
We can subtract two pointers only. This is because subtracting a pointer from a number does not make any sense. The vice-versa is quite possible though.
Adding two pointers is no problem.
We can apply relational tests to pointers. Eg. p<q will be true if p points to a lower address then q.

Array of Pointers:
Just like with any other data type we can have a whole array full of pointers. The purpose is still the same, pointing at a lot more than one thing but without having to name each pointer separately. To have 10 pointers with the similar name :
int *p[10];
Now to make the ith element of the above array to point to some variable do :
p[i]=&variable;

Pointers to Arrays:
This time there is one pointer that will point to a whole array. Remember a pointer can point to any thing but it's the base type that limits how many addresses the pointer can control at one time. This principle is used when pointing to arrays using pointers.
Suppose an array int A[10];
Now we want the address of the first element of this array, its easy right?
&first_element : &A[0] 
But C++ has a shorter way to do the same thing:
&A[0] means the same as A (That's right. The name of the array alone, without any brackets). Now we can have a pointer point to the first element of the array using:
int *p;
p=A ; //instead of p=&A[0]
Now keeping the above code in mind, think about how you would access elements of the array? Here is how:
A[3] is the same as *(p+3); 
This works like this: p is a pointer containing an address and adding 3 to p means jump three addresses ahead of the address specified by p it self and the * means that get the value stored at that address is being referred to. Hence it means the same as A[3].
This very concept can be extended to multidimensional arrays as well. So again assume : 
int *p,arr[m][n];
p=arr; //same as p=&arr[0][0]
Now accessing certain element of arr means jumping to it from the first address, so arr[i][j] can be accessed like:
*(p+no. of address units to jump to get to the element);
This changes to:
*(p+i(total no. of columns)+j)==*(p+i.n+j); 
But there is one little change, to make the pointer arithmetic work properly we have to add another thing: (type*), so a[i][j] is actually equal to *((int*)a+i.n+j). ALWAYS ADD THE (int*) in the beginning. It is important. This is basically an cast that converts a two dimensional array into an int pointer type. Check out the following to understand better:
int a[10][10];
int *p;
Unlike in normal array cases, we cant do p=a; This will produce an type cast error. We can't convert an int to an int* type so we have the cast the int into an int* as:
p=(int*)a;
Similarly for a two dimensional array we can use a pointer to reduce it into a one dimensional array as:
int a[10][10],*p;
p=(int*)&a[1][]; 
This means that p now points to the first row of a. Hence p+i will point to the ith element of the array at the 1st position in a (remember a is an array of arrays).
We can excess each element as *(p+i) and then to point to the next array all we have to do is p=p+(index*row_length) and p will then point to the array at the index specified.



Passing arrays as an argument to functions:

It is not possible to pass an entire one dimensional array as an argument to a function in c++. But the same effect can be achieved by being creative in a number of ways. First thing first, we have to prepare a function while defining it in such a way that it is ready to receive an array as its argument. So we play with the parameters of the function in the following 3 ways:
1) int func(int arr[10]){...}
2) int func(int arr[]){...}
3) int func(int *p){...}
In all the above cases the function func() now accepts an address to the first element of an int array (to be more precise though, our function can work with any address as its argument). Now let us give our function some address as its argument:
func("some address"); 
Now based on this the function will react in three different ways depending on its declaration.
1) If the function was declared in this fashion, the address passed as the argument will be the address of the first element of the int array of size 10 and can be used inside the function body.
2) The address is now the address of the  first element of this un-sized array.
3)The address is passed on to this pointer p. p now points to this address and hence can be used in any way a normal pointer could be used, but for our purpose we can use it to manipulate an array.

Now imagine an array int haha[10]. Simply do the following to pass this array as an argument to our function:
func(haha);
Remember we are just passing an address to our function (haha means &haha[0]). Hence the changes made by the function will reflect in the original array it self.
When a two-dimensional array is used as an argument to a function, only a pointer to the first element is actually passed. However, the parameter receiving a two-dimensional array must define at least the size of the rightmost dimension. (You can specify the left dimension if you like, but it is not necessary.) The rightmost dimension is needed because the compiler must know the length of each row if it is to index the array correctly. For example, a function that receives a two-dimensional integer array with dimensions 10,10 is declared like this:
int func(int arr[][10]){...};
Similarly When passing multidimensional arrays into functions, you must declare all but the leftmost dimension in the parameter list of the function.


Pointers to Functions:
I know what you are thinking. A pointer points to a variable, big or small. Since when have they started pointing to functions as well? But remember the only thing that a pointer does is point to something in memory. All it cares about is an address to start with and a limit to how many addresses following the starting one is it supposed to handle at a time. Consider a function:

int add(...)
{}
This function too has an address in memory which can be pointed to by a pointer. Just like in an array the name of the function 'add' alone is actually a shorthand of specifying the starting address of the memory block which defines this function. So we can use the following two things interchangeably:
add means &add(...)
The pointer pointing to a function must have the same type as the return type of the function it is pointing to. But unlike the normal pointer declaration, we have to specify the number and type of arguments the function to be pointed is expected to have. So for example if we have to declare a pointer which points to the following function:
int add(int a, int b);
We will doing something like this:
int (*p)(int,int);
The above statement means that the pointer p points to a function that returns an int value and has two arguments both being int in this case. Basically it tells the compiler to reserve that much space so as to occupy the function arguments as well for the pointer so that it can store and use these arguments.
Now our pointer p is ready to point to the add function:
p=add; // evaluates the same as this illegal but meaningful statement : p=&aff(int,int);
The main use of this tool is to pass a function as an argument to another function. The function which is expected to have a pointer to another function as its argument should be defined in a special manner. For example :
int big_function(int a,int (*ptr)(int,int));
This means that this function is expecting a pointer which points to a function returning an int and having two int arguments. 
big_function can use ptr like this:
int big_function(int a,int (*ptr)(int,int))
{
return a+(*ptr)(2,3); // Basicaly add(2,3) can be called as                  (*ptr)(2,3) assuming that ptr points to add following the                    manner explained above.
}
Remember, its the calling function's duty to give actual values as arguments to our pointed function inside its (calling function's) body.
The use of () around *ptr when calling the function it points to is not necessary but is popular as it directly tells that a function has been called using a pointer. Another simpler syntax of doing the same thing inside the calling function would be:
ptr(2,3); 
But this can sometimes confuse us as we might think that we are calling a function named ptr but actually we are not. So instead of "return a+(*ptr)(2,3);"
We could also have used 
"return a+ptr(2,3);"
Another thing to consider is that we could have eliminated the use of a pointer completely in the first place in the above example by calling the big_function as: 
big_function(3,add);
The declaration of the big_function of course will remain the same.
This whole concept can be used as an alternative to switch statements where for every different case we call different function like in a calculator program. Using this we can simply create an array of function pointers and call the required function by altering the index of the array.
Overloaded functions can also assigned to a pointer. Care should be taken as to how the pointer is defined argument wise so as to be clear on which overloaded version of a function is the pointer is supposed to point to.
Pointer to Objects of Classes/ Structures:
These things are relentless, like zombies. There is hardly anything in the language to which a pointer can't point to. You don't believe me? Consider this then:
class abc
{};
abc is a nice and healthy class, sitting there waiting to be instantiated. 
abc obj; //instantiated
obj.member(); //good old member call using an object
I wonder what is wrong with this? Seems perfectly fine and potent to me. But no, we must use pointers somehow. Otherwise whats the point. Firstly we declare a pointer of the base type abc:
abc *ptr;
So far it does not point to anything. Hungry pointers (like zombies) are not a good thing. We must keep them fed and occupied. So let's create a new object dynamically and feed it our zomby. 
ptr=new abc();
Now calling a member can be done like:
(*ptr).member();
There is shorthand for this though (of course there is)
ptr->member();
So :
(*ptr).member()  is the same as ptr->member();
The same can done with the members of a structure.
I would like to give a little insight on why do we need this. Why can't  we use the more clean way of dealing with objects, you know, the one involving no pointers. The answer is: "Dynamically created Objects". Sometimes one doesn't know how many objects of a class are needed by the program to do its job. In those cases, objects are created and destroyed dynamically. This is done easily enough using the new and delete operators. The problem is that we can't name these objects before hand because we don't know how many of them are going to be there in our program. And if we can't name them, how can we write code that uses them? This is where pointers come to our rescue. It doesn't matter if an object has a name or not, it will surely have an address. And all we need to do then is to keep a pointer ready to point to the address of this dynamically created object. And using this pointer then, we can put our object to work. And since one pointer can be used to point to more than one thing (one at a time of course), we are free to use this  pointer with as many dynamically created objects as we like.

The this pointer:
Refer to the article on this link:this link
Pointers to derived types:
Assume A is the base class and B is derived from A. A pointer of type A* can point to an object of class B. This is an important exception to the rule that pointers of one type can not point to objects of other types. The reverse is not possible though. The important thing to know is that the pointer of the base class can be used to access only those members of the derived class that were inherited from the Base class.
class A
{
int i;
};
class B: pubilc A
{
int j;
};
Now,
A *p;
B obj;
p=&obj;
p->i // will work
p->j //will not work
In order to gain full access to the members of the derived class via the pointer of the base class, we can cast the base class pointer into a derived class pointer type.
((B *)p)->j //now this will work.
You should not however, try and increment a base class pointer, because it will then not point to the next derived class object. The pointer arithmetic will be in accordance to the base class, hence this will cause problems.
The above concepts are used for run time polymorphism using virtual functions.


Pointers to class members:
We have assigned regular pointers to objects of class. And using these pointers and the arrow operator we were able to access the members of objects. But in C++, there is a special pointer, which points only to a class member and not to any object of that class. This type of pointer is called pointer-to-class. Since this pointer is all together different from the regular pointer, it has to be given a separate set of operators to work with as well. '.' is replaced by '.*' and '->' is replaced by '->*'.
Suppose an object of size 50, starts at address 300. Suppose that a member of this object is at the 20th position in the whole object space. pointer-to-member takes advantage of this little offset knowledge to access the members alone of the object. So basically, the ptm will now point to the address 300+20, in order to access the desired member.
Consider:
class A
{
public:
int i;
A(int x) {i=x;}
int doubleIt()
{
return 2*i;
}
};
Declaring a ptm for the member i in the class A:
int A::*p; 
int is there because p is intended for an integer, A:: is there because p is special pointer, it points to a member of class A. The member is not specified as of yet.
Setting an offset within the class for p:
p=&A::i;  
This tells that p is supposed to point to member i of any object of class type A.
Similarly:
int (A::*d)(); 
This is a pointer to a function, a member function. The () in the end separates the two: pointer to member and pointer to member function.
d=&A::doubleIt() //assigning a member to d.
The following code segments can hopefully draw out some guideline
on how to use these type of pointers:
A obj1(1), obj2(2);
cout<<obj1.*p   // will print 1
cout<<obj2.*p   //will print 2
cout<<(obj1.*d)() //will return 2
cout<<(obj2.*d)() //will return 4
So basically, we kind of have (not really though) created members of the class. Members that are pointers. Pointers to other fellow members. So, obj.*p means, access some member of obj (obj.) and *p means, the newly made member, the pointy one.
Now instead of having obj, we can have a pointer to obj. And then through this pointer we can access these pseudo members of obj.
A *ptr=obj;
cout<<ptr->*p // Easy isnt it?

Difference between regular Pointers and PTMs:
A obj;
int *p;
p=&obj.i; 
The above example has a regular pointer pointing to some int, in this case a member of an object. p can point to only this member of only this object. 
int A::*p;
p=&A::i;  
PTM, can point to the member i of any object of class A from now on wards.
Basically, regular pointers just contain a singular address, hence they will always point to only one thing. But PTMs contain offsets within an object, hence they can be used to point to members of any objects. Its like applying the offset to different objects.

No comments:

Post a Comment