Archive

Archive for the ‘Programming’ Category

Pointers in C

November 15, 2011 3 comments

Greeting to all!!!

Hope you all are having a great time. But for “C” lovers, the time is going to become more better. C language is the most simplest of programming languages with just 32 keywords defined. But still, the tricks a C program can contain has unlimited possibilities. Here we unravel one such part.

Most of the typical question asked in any technical exams with “C and Data Structures” as its course comprise of Pointers and Trees. With Pointers as the building blocks of such data structures, problem often get complex when actually they are not.
So here, lets brush up the concept of Pointers, and see whether those problems are really that hard or not.

Note: While writing this article, I have considered that you all already know about primitive variable declarations in C.

Pointers are just a variable, with a special property that it saves the reference of other objects in the program.

Syntax for declaration of a pointer variable:
<datatype> * <pointer_name>;

Ex.   int *p;
float *q;

Pictorially, these statements are nothing more than this:

image          image

And since references are nothing but memory addresses, so all pointers irrespective of the datatype specified with them, they stores similar kind of data( a memory addresses which are in form of unsigned integer type). And sizeof() for all pointer variables return equal value, again irrespective of the datatype specified. That is, even though the variable of character and integer require different size of memory block to store data, pointer variable of both datatype require equal amount.

So what is the need of specifying data type in pointer declaration??

The datatype provided with pointer declaration serves two purposes:

  1. To specify the base of pointer variable used during the arithmetic operation performed on them, usually increment/decrement operations.
  2. Secondly, it is used during the resolution of the data pointed by the pointer. How many block of memory to read, to correctly fetch the data.
    Before we go further in details of pointers, you need to get familiar with two operators and two terms as well.

The operators are * and &.

      1. * Operator – It has two uses. Firstly, it is used in declarations to specify that the variable being declared is a pointer variable. Secondly, when used with a variable in an expression it is used to fetch the data stored at the memory location specified by the variable.
      2. & operator- It is also called address operator, as it is used to fetch the memory address associated with the variable specified.
        E.g.  int x=6;  //declares a block on integer in memoryassignment of pointer variables occurs as
        int *p;
        p=&x;   // returns the address of the block named ‘x

        now to print the value of x,either of the two can be used
        printf(“%d”,x);  or  printf(“%d”,*p);

This operation can be pictorially shown as:

image

The two term that need to be get familiar with are

  1. Lvalue  –  Those values which are only permitted on the left side of the assignment operator( =). Such values have the property to be resolved to some memory block or placeholders for storing data. Permitted values are variables.
    In reference to the following declarations,
    int x;
    int *p;

correct statements:
x=6;
*p=29;

These statements as these are incorrect:
2=x;    //  incorrect. “compiler error : lvalue required”. constants are not lvalues.
&x=29; // same as above. as “&x” is resolved to memory address of x, a constant value.

  • Rvalue –  These values are permitted to exist on the right hand side of assignment operator( =). Valid Rvalue are constants and memory references.

    Thus it can be said that with any variable there are two kinds of values associated, an lvalue and a rvalue.

    Considering a memory tape, and variable as a memory blocks

 

image

When the variable is present on the right hand side of the assignment operator( =) it is resolved to its lvalue to accept( or store) the value returned. And when the variable appears on the right hand side of the assignment operator( =) it is resolved for its rvalue.

Pointer variables are used to store lvalues.

Lets get hands on some codes.lets take a look at how pointers are actually used.
(The codes written here are written, with the parts of program not relevant with the concept described here, but must be included while execution.)

Pointer Representation in memory


Consider this piece of code:

int a=6;
int *b;
b=&a;
*b=10;

So,what are the values of a, b, &a, &b, *b, *a?

To answer this problem, lets take a visual look of the process going on here
Step 1: “int a=6;” declares a variable named “a” at memory location 500(assumed).

image

Step 2: “int *b;” declares a pointer variable of integer type named “b” at memory location 600(assumed).

image

Step 3: “b=&a;” resolves the memory address of block “a” and stores it in “b”.

image

Step 4: “*b=10;” now since “b” is a pointer variable pointing to “a” the value is stored at memory block pointed by “b”” that is “a” is modified to store new value.

The situation was like this”:

image

and now it becomes:

image

Again, a look at the question:

what are the values of a, b, &a, &b, *b, *a?

Answer:

a=6            //value assigned to a
b=500       //memory address of a(it was assigned)
&a=500     //memory address of a(resolved. its not an assignment statement)
&b=600     // memory address of b(resolved)
*a             // invalid statement. a is not a pointer variable.
*b=10       //value stored at memory block pointed by b.

You can also modify value stored in “a” using pointer “b” with a statement like
*b = (*b) + 10;
*b= (*b) * 2;       // this statement uses *- unary operator foe dereferencing as
//well as *- binary operator for multiplication.

 

(*) is a unary operator and requires an lvalue to operate. Similarly (&) is an unary operator that requires a non-constant value to operate upon. An object that has an lvalue associated with it.

The statements like *2 and  &5 are invalid. Dereferencing(*) operator used with a variable of primitive datatype is also an invalid operation.

 

Operations on pointers


While declaring the pointer we have to mention a datatype, but what all pointer variable does is store a memory address. What’s with the datatype then?

The answer to the above question can only be understood when you consider operations on pointer variables.

When we assign a block’s memory address to a pointer variable, only the address of the first memory block gets stored in pointer variable. So for character variable the memory block allocated is o block. 2 memory block together forms an integer variable. 4 memory block form a float variable. So when we dereference a pointer variable using (*) operator, these datatypes helps compiler to understand how many blocks of memory to read, to correctly read the data stored.

Increment and Decrement operators also behave differently for pointers. The actual increment in the value of pointer variable is determined by the datatypes provided. Increment and decrement is done in terms of next or previous memory blocks of similar datatype to point to.

for example, considering sizeof(int)=2.
if int *b;

and b has a value stored(say 500) then
statements:
(b+1)    // returns 502.
(b-2)     // returns 496.
(b++)    // changes the value of b to 502.
(b—)     // changes the value of b to 498.

This is all that was needed to understand the pointers.

Some more details


Lets take a look at a few more concepts using the similar concepts but worth mentioning.

There are several types of Pointers:

  • Pointers to a variable of primitive Data type.
    e.g. int *p;
  • Pointer to an array(single dimensional/ multidimensional).
    e.g. int *p=&a                              // “a” is an array variable
    int (*p) [num_row]                //  “p” is pointer to pointer array
  • Pointer to a structure
    e.g. struct node *ptr=&node1;   // “node1” is a variable of struct node type
  • Pointers to a function.
    e.g. int (*fp)(int,int)=add;     // “fp” stores the return address of the function
  • Void pointers.
    e.g. void *vp=(int *)&i;
  • Pointer to a pointer.
    e.g.  int **p;                         // “p” is a pointer to a pointer of integer type.

Void pointers are special pointers with no datatype specified. It can store address of any datatypes but arithmetic operations like increment/decrement operations are not allowed on void pointers. Neither (*) operators are permitted to operate on Void pointers

Void pointers are used to pass references to functions. or store some references temporarily for other operations.

Pointers to pointers are special pointers which points to other pointers

      . Such pointers often

comes in handy when dealing with array of pointers

    . So conceptually its even possible to declare a pointer to pointer to pointer to an int. and 4 levels. and even more.

C compiler puts a limitation on the number of dereferencing operation performed in a single statements to 12 operations.

This limitation is only because of performance issues and for no other issues.

Some Example codes and Explanations :


Code 1:

#include<stdio.h>
void main()
{
char *p=”hai friends”,*p1;
p1=p;
while(*p!=”) ++*p++;
printf(“%s %s”,p,p1);
}

Answer:
                 ibj!gsjfoet

Explanation:

++*p++ will be parse in the given order

  • 1. *p that is value at the location currently pointed by p will be taken
  • ++*p the retrieved value will be incremented
  • when ; is encountered the location will be incremented that is p++ will be executed

Code 2:

#include<stdio.h>
void main()
{
static char names[5][20]= {“pascal”,”ada”,”cobol”,”fortran”,”perl”};
int i;
char *t;
t=names[3];
names[3]=names[4];
names[4]=t;
for (i=0;i<=4;i++)
printf(“%s”,names[i]);
}

Answer:
Compiler error: Lvalue required in function main

Explanation:

Array names are pointer constants. So it cannot be modified. The reason being when you declare an array a block of memory is allocated that very instant, which need not be changed through out the program.

 

Code 3:

#include<stdio.h>
void main()
{
void *v;
int integer=2;
int *i=&integer;
v=i;
printf(“%d  %d”, *i ,*v);
}

Answer:
Compiler Error. We cannot apply indirection on type void*.

Explanation:

Void pointer is a generic pointer type. No pointer arithmetic operation can be performed on it. Void pointers are normally used for,

  • Passing generic pointers to functions and returning such pointers.
  • As a intermediate pointer type.
  • Used when the exact pointer type will be known at a later point of
    time.

Code 4:

#include<stdio.h>
void main()
{
int i=5;
printf(“%d”,++i++);
}

Answer:
Compiler error: Lvalue required in function main

Explanation:

++i yields an rvalue. For postfix ++ to operate an lvalue is required.

 

Code 5:

1. const char *a;
2. char* const a;
3. char const *a;

-Differentiate the above three declarations.

Answer:

  • ‘const’ applies to char * rather than ‘a’ ( pointer to a constant char )
    *a=’F’ : illegal
    a=”Hi” : legal
  • ‘const’ applies to ‘a’ rather than to the value of a (constant pointer to char )
    *a=’F’ : legal
    a=”Hi” : illegal
  • Same as 1.

Now, that was interesting, wasn’t that. I am sure for those who already knew Pointers must have discovered some new facts and those who were afraid of Pointers, now know, Pointers aren’t really that hard.
Solve more problems because with each new problem you solve, you improve the understanding of the concepts you have.( That’s a key to everything out there, to learn ).

So get yourself geared up, because I am definitely sure that now you will be able to solve half of the questions from any C aptitude paper. More than half the questions are from  pointers.
Do Try some more problems!!

Crap!! End-Semesters knocking…

Have a nice time, you all.
signing off for now!!

Kr. Abhishek

Categories: Programming
Design a site like this with WordPress.com
Get started