Monday, October 6, 2008

How to Declare pointers ?

just like other variables, you have to declare pointers before you can use them. Pointer declarations look much like other declarations: but don't be misled. When pointers are declared, the keyword at the beginning (c int, char and so on) declares the type of variable that the pointer will point to. The pointer itself is not of that type, it is of type pointer to that type. A given pointer only points to one particular type, not to all possible types. Here's the declaration of an array and a pointer:
int ar[5], *ip;
We now have an array and a pointer (see Figure):
 
An array and a pointer
The * in front of ip in the declaration shows that it is a pointer, not an ordinary variable. It is of type pointer to int, and can only be used to refer to variables of type int. It's still uninitialized, so to do anything useful with it, it has to be made to point to something. You can't just stick some integer value into it, because integer values have the type int, not pointer to int, which is what we want. (In any case, what would it mean if this fragment were valid:
ip = 6;
What would ip be pointing to? In fact it could be construed to have a number of meanings, but the simple fact is that, in C, that sort of thing is just wrong.)
Here is the right way to initialize a pointer:
int ar[5], *ip;ip = &ar[3];

In that example, the pointer is made to point to the member of the array ar whose index is 3, i.e. the fourth member. This is important. You can assign values to pointers just like ordinary variables; the difference is simply in what the value means. The values of the variables that we have now are shown in Figure 5.4 (?? means uninitialized).
 
Figure Array and initialized pointer
You can see that the variable ip has the value of the expression &ar[3]. The arrow indicates that, when used as a pointer, ip points to the variable ar[3].
What is this new unary &? It is usually described as the ‘address-of’ operator, since on many systems the pointer will hold the store address of the thing that it points to. If you understand what addresses are, then you will probably have more trouble than those who don't: thinking about pointers as if they were addresses generally leads to grief. What seems a perfectly reasonable address manipulation on processor X can almost always be shown to be impossible on manufacturer Y's washing machine controller which uses 17-bit addressing when it's on the spin cycle, and reverses the order of odd and even bits when it's out of bleach. (Admittedly, it's unlikely that anyone could get C to work an an architecture like that. But you should see some of the ones it does work on; they aren't much better.)
We will continue to use the term ‘address of’ though, because to invent a different one would be even worse.
Applying the & operator to an operand returns a pointer to the operand:
int i;
float f;
    /* '&i' would be of type pointer to int */
    /* '&f' would be of type pointer to float */
In each case the pointer would point to the object named in the expression.
A pointer is only useful if there's some way of getting at the thing that it points to; C uses the unary * operator for this job. If p is of type ‘pointer to something’, then *p refers to the thing that is being pointed to. For example, to access the variable x via the pointer p, this would work:
#include 
#include 
main(){
    int x, *p;

    p = &x;         /* initialise pointer */
    *p = 0;         /* set x to zero */
    printf("x is %d\n", x);
    printf("*p is %d\n", *p);

    *p += 1;        /* increment what p points to */
    printf("x is %d\n", x);

    (*p)++;         /* increment what p points to */
    printf("x is %d\n", x);

    exit(EXIT_SUCCESS);
}
Example
You might be interested to note that, since & takes the address of an object, returning a pointer to it, and since * means ‘the thing pointed to by the pointer’, the & and * in the combination *& effectively cancel each other out. (But be careful. Some things, constants for example, don't have addresses and the & operator cannot be applied to them; &1.5 is not a pointer to anything, it's an error.) It's also interesting to see that C is one of the few languages that allows an expression on the left-hand side of an assignment operator. Look back at the example: the expression *p occurs twice in that position, and then the amazing (*p)++; statement. That last one is a great puzzle to most beginners—even if you've managed to wrap your mind around the concept that *p = 0 writes zero into the thing pointed to by p, and that *p += 1 adds one to where p points, it still seems a bit much to apply the ++ operator to *p.
The precedence of (*p)++ deserves some thought. It will be given more later, but for the moment let's work out what happens. The brackets ensure that the * applies to p, so what we have is ‘post-increment the thing pointed to by p’. Looking at Table 2.9, it turns out that ++ and * have equal precedence, but they associate right to left; in other words, without the brackets, the implied operation would have been *(p++), whatever that would mean. Later on you'll be more used to it—for the moment, we'll be careful with brackets to show the way that those expressions work.
So, provided that a pointer holds the address of something, the notation *pointer is equivalent to giving the name of the something directly. What benefit do we get from all this? Well, straight away it gets round the call-by-value restriction of functions. Imagine a function that has to return, say, two integers representing a month and a day within that month. The function has some (unspecified) way of determining these values; the hard thing to do is to return two separate values. Here's a skeleton of the way that it can be done:
#include 
#include 
void
date(int *, int *);     /* declare the function */

main(){
    int month, day;
    date (&day, &month);
    printf("day is %d, month is %d\n", day, month);
    exit(EXIT_SUCCESS);
}

void
date(int *day_p, int *month_p){
    int day_ret, month_ret;
    /*
     * At this point, calculate the day and month
     * values in day_ret and month_ret respectively.
     */
    *day_p = day_ret;
    *month_p = month_ret;
}
Example
Notice carefully the advance declaration of date showing that it takes two arguments of type ‘pointer to int’. It returns void, because the values are passed back via the pointers, not the usual return value. The main function passes pointers as arguments to date, which first uses the internal variables day_ret and month_ret for its calculations, then takes those values and assigns them to the places pointed to by its arguments.
When date is called, the situation looks like Figure
 
Just as date is called
The arguments have been passed to date, but in main, day and month are uninitialized. When date reaches the return statement, the situation is as shown in Figure (assuming that the values for day and month are 12 and 5 respectively).
 
Figure 5.6. Just as date is about to return
One of the great benefits introduced by the new Standard is that it allows the types of the arguments to date to be declared in advance. A great favourite (and disastrous) mistake in C is to forget that a function expects pointers as its arguments, and to pass something else instead. Imagine what would have happened if the call of date above had read
date(day, month);
and no previous declaration of date had been visible. The compiler would not have known that date expects pointers as arguments, so it would pass the int values of day and month as the arguments. On a large number of computers, pointers and integers can be passed in the same way, so the function would execute, then pass back its return values by putting them into wherever day and monthwould point if their contents were pointers. This is very unlikely to give any sensible results, and in general causes unexpected corruption of data elsewhere in the computer's store. It can be extremely hard to track down!
Fortunately, by declaring date in advance, the compiler has enough information to warn that a mistake has almost certainly been made.
Perhaps surprisingly, it isn't all that common to see pointers used to give this call-by-reference functionality. In the majority of cases, call-by-value and a single return value are adequate. What is much more common is to use pointers to ‘walk’ along arrays.

No comments: