Wednesday, 18 November 2015

What they Don't Teach you in C, but always appears in Exams - I

It's the same story, every year and every semester. Everyone teaches or studies lethargically throughout the semester and tries to rush through the entire syllabus in a few weeks. It's common; so common that a few very important topics and chapters are left untaught and it's the student, who is befuddled by the strange questions in the Question Paper. Phrases such as, 'From which chapters, were those 12 Mark Questions?' are commonplace.


                 Although, we sincerely hope that, it hasn't happened to you, but at the off-chance that it has, we will be discussing a few topics that we believe are indispensable and are usually not covered by your teachers:

Pointers in C


A pointer is a variable that contains the memory location of another variable. It is a variable that represents the location of a data item, such as a variable or an array element or a function.Let us take a look at the following code to understand how a pointer works: 

    int x = 8;
    int *ptr = &x;
    printf("x = %d",x);
    printf("\tptr = %d",*ptr); 


This code produces the following output:

    x = 8              ptr = 8

Pretty simple, isn't it? You just pass on  the memory address of a variable to variable whose name starts with an asterisk '*' and now, that variable becomes equal to the original variable!

Wrong!!!

That's not remotely what pointers do. A pointer takes the address of the variable and that's it. It's the asterisk '*', also called indirection operator, that fetches the data stored at that address. You don't believe me, try printing without '*', and you'll see!
Now, that we know what a pointer does, the next question that comes to mind is how do we declare and use it? And the answer to it is simple. The syntax for declaration of a pointer in C is 
<data_type> *<pointer_name>;  
e.g. int *x;  char *ch; float *f; etc.    
And now turning to how do we use it? To use a pointer, you must pass the address of alegitimate variable (not a pointer) of the same data type to the pointer. e.g. the statements

        float f = 3.14;
    char *ch = &f; 


are absolutely illegal! Why you ask? Because the size of a pointer is 1 byte (ANSI C 1989), irrespective of it's data type. If an integer is stored in memory at the addresses 5001 and 5002, then the pointer only stores the address 5001, for it can store only one byte. What the data type does is tells the indirection operator how many bytes it needs to read in order to fetch the data stored. So, the statement *pointer tells the compiler that it needs to read the data at 5001 and 5002 in order to read the integer stored. Now. we'll move onto our next topic:

Pointer Arithmetic


The above table describes the operations that can be performed on pointers (without the indirection operator '*', for with the indirection operators, the arithmetic is on the data rather than address)

We'll come to pointer arithmetic later. Let us take a look at how regular arithmetic can be performed on variables, via pointers, first!

So, below is a C Program to add two integers using pointers:

#include <stdio.h>

main()
{
    int num1, num2, sum;
    int *ptr1 = &num1, *ptr2 = &num2, *psum = &sum;;
    scanf("%d %d",ptr1,ptr2); /*why ptr1 and not &ptr1?     Because ptr1 = &num1, and that's what we need to pass       with scanf*/
    *psum = *ptr1 +*ptr2;
    printf("\nsum = %d",*psum);
    return 0;
} 

Output : 4 5
         sum = 9

Let us see some more examples:

1. Code to find the area of a circle 

#include <stdio.h>

main()
{
    int rad, area;
    int *prad = &rad, *parea = &area;
    scanf("%d",prad);
    *parea = 3.14 * (*prad) * (*prad);
    //why (*prad), because * is also the multiplication operator
    //so, you need to case the indirection operator and pointer in parenthesis(brackets)
    printf("\narea = %d",*parea);
    return 0;

}

Output : 7
         area = 153.86

2. Code to find the largest of three numbers

#include <stdio.h>

main()
{
    int num1, num2, num3;
    int *p1 = &num1, *p2 = &num2, *p3 = &num3;
    printf("\nEnter three numbers : \t");
    scanf("%d %d %d",p1,p2,p3);
    if(*p1 > *p2 && *p1 > *p3)
        printf("\n %d is the largest",*p1);
    else if (*p2 > *p3)
            printf("\n%d is largest",*p2);
    else
        printf("%d is largest",*p3);
    return 0;
}

Output:  Enter three numbers :  2 14 5
         14 is largest

3. A code to print all the even numbers between two bounds (m and n)

#include <stdio.h>

main()
{
    int m, n, i;
    int *p1 = &m, *p2 = &n;
    printf("\nEnter the upper and lower bounds :\t");
    scanf("%d %d",p1,p2);
    for(i = *p1; i < *p2; i++)
    {
        if(i % 2 == 0)
            printf("%d\t",i);
    }
    return 0;

}

Output : Enter the upper and lower bounds : 4 14
         4  6  8  10  12

Null Pointer

A null pointer is a special pointer value that doesn't point anywhere i.e a null pointer doesn't carry (or points to) any valid memory address. To declare a null pointer, we may use the Preprocessor  constant NULL defined in the header files <stdio.h>, <stdlib.h> and <string.h>.

It is used in evaluating the functions or structures that return a NULL value i.e No return when the operation fails. e.g. 
Mathematical underflow in Queues is evaluated by using null pointers, as:

if(front == NULL)
{
    printf("\nUndeflow");
    exit(0);
}

Generic Pointers

A special type of pointer, of the data type void, that can point to any data type is called a generic pointer. It is declared like a normal pointer, but with the keyword void. e.g.  void *ptr

But, since C doesn't allow variables of void data type, we need to type cast a void pointer, every time it is used.

Note : The type casting is to be done with pointers of intended types

Let's take an example to understand it better:

#include <stdio.h>

main()
{
    int x = 65;
    char ch = 'P';
    void *gp;
    gp = &x;
    printf("\nValue of void pointer with int = %d"*            (int*)gp);
    gp = &ch;
    printf("\nValue of void pointer with char = %c",*          (char*)gp);
    return 0;

}

Output : Value of void pointer with int = 65
         Value of void pointer with char = P

Now, if some of you are wondering, what the hell did I do before passing gp (void pointer) to printf(), then you need to read the Note above, written in bold, again. It was said, that you need to typecast a void pointer with a pointer of the data type, whose address is passed onto it. In case, all the asterisks are confusing you, here's a break down -
The first asterisk is the indirection operator, and the asterisk in the bracket, after the keyword int represents a pointer. In case, you don't know what type casting is, you are gonna be in big trouble at your exams.

The use of generic pointers is widely discouraged, for they are not type checked by the compiler, and cause some serious incompatibility and run time errors like : _out_of_range, _too_long and _incor_type. And still, they are a part of most university syllabuses. You'll almost never use void pointers, but it is presumed that it will be good for you to know that you can turn to them if you want to point a different data types at different times.

Why use pointers?

From all we have seen up to this point, it seems as though all we have accomplished with pointers is 'Aliasing'. But wait, haven't we done that already using something like this : int x = &y ?

Yes we have, and also that's not the job of pointers, or atleast that's not what pointers are for anyways. Just ask yourself this question:

Your friend who moved to a different city is back in town, after a long time and doesn't remember the city as well as he used to before. He also wants to meet everyone at their houses. So what do you think would be easy, bringing everyone's house to one place OR giving him their addresses? 

This concept may seem a little foggy at this moment but with computers, maintaining actual copies of all the variables in large data structures, is equivalent to, copying someone's entire house : it's interiors, furniture, paint and everything, in your house. it also saves a ton of memory, that may prove to be very crucial at run-time. As we move onto more complex, user-defined data types, pointers become an indispensable part of our code. You'll find several applications for pointers (that you'll need to memorize and regurgitate in your exams) in your books; we are not going to discuss that.

That's it for this first part in 

What they Don't Teach you in C, but  always appears in Exams 

We'll be with our second part tomorrow. where we'll discuss Pointers and Arrays, Matrix Operations using Pointers and Structures.





No comments:

Post a Comment