Pointers

A Pointer is a special type of variable that holds the address as a data item in it. The address may be of one of these: variable, array, function, structure or another pointer.

Usually, an ordinary variable holds a number or a character as a data item in it. Instead, pointer holds the address. If a variable occupies more than one byte, pointer holds the first byte (or byte0) address. As this is a variable, the address in a pointer can be changed. Mostly, pointer uses 2 or 4 bytes to hold the addresses. In short, a pointer variable is a group of cells that can hold an address.
Pointer is  an address; not any ordinary value

Advantages of pointers


  • Pointers are used frequently in C, as they offer a number of benefits to the programmers. These include: 
  • Pointers can be used to access and manipulate data stored in memory directly.
  • Pointers allow C to support dynamic memory allocation.
  • Pointers provide an efficient way to create and manipulate efficient data structures such as stacks, queues, linked lists, trees and graphs.
  • Pointers increase the execution speed and thus reduce the program’s execution time.
  • Pointers are more efficient in handling arrays and data tables.
  • The use of pointer arrays to strings results in saving of data storage space in memory.
  • Pointers allow to change the calling function’s arguments and to return multiple values from called function.
  • Pointers permit references to functions and thereby facilitating passing of functions as arguments to other functions.

The real power of C lies in the proper use of pointers. The misuse of pointers causes serious problems such as system crash.

11.3. Referencing operator (&)
Referencing operator is a unary operator. It acts on one operand at a time. It returns the address of operand. The operand must be a named region of storage (like int variable, array variable, pointer variable etc.) for which a value may be assigned. The operand must not be a constant or an expression or a register variable. This operator is also called as “address of” operator.

The & operator should be preceded with the operand in order to return the address.


Valid expressions:     &a    //where a is the name of variable.
                                     &c    //where c is the name of an array.
&fact //where fact is the name of function.

Invalid expressions:   &253  //constant can’t be used as an operand.
                                    &(a+b) //expression can’t be used as an operand.

&r //register type can’t be used as an operand.


Consider the declaration:

int a=20;
This declaration tells the C compiler to:
a) Reserve the required space in memory to hold the integer value.
b) Associate the name a with this memory location.
c) Store the value 20 at this location.
Suppose that the variable a occupies 2 bytes on 16-bit computer. This can be represented as follows:





From this memory map, it is clear that the data item 20 can be accessed with the help of name of variable as well as address of variable (or memory location or reference).
The conversion character %x is used to print address in hexa-decimal notation. The conversion character %u is used to print the address in decimal notation (especially, a positive number to understand easily).

Program to print the address of variable in hexa decimal and decimal notations

#include
main()
{
int a=20;
printf(“\n  The address of a =%x”,&a); //prints address in hexa decimal notation
printf(“\n The address of a=%u”,&a);  // prints the address as a positive long integer
}

Dereferencing operator (*)
Dereferencing operator is a unary operator. It acts on one operand at a time. It returns the value stored at the address of operand. The operand must be an address, a pointer that holds address or a pointer expression. This operator is also called as “value at address” operator or indirection operator.
The * operator should be preceded with the operand in order to return the value at address of operand.



Valid expressions:   $*(\& $num)   //where num is a variable
$*\& *\& $num
$**\& $num

Invalid expressions: 
                                        *2009    //constant value can’t be used as an operand.
                                        *num    //only addresses or pointers or pointer expressions can be used.
($*\& $)num //parentheses should not enclose two unary operators

Working with pointers:

In order to work with pointers, do the following:
1. Declare a pointer variable
2. Initialize it.
3. Access the original value through a pointer or perform any other operation on it.

Declaration of a pointer variable: Just like an ordinary variable, a pointer variable should be declared before it is used. This declaration has the following form:




In this syntax,

$ < $storage_class$ > $ is any one of the auto, static, extern or register.
$ < $data typte$ > $ is any one of int, float, char, double or void or data types such as signed int, long int etc,
The symbol * should be preceded with the name(s) of pointer(s).
<$ < $pointer_name$ > $ is any valid identifier. If there are more pointer_names, those should be separated with commas.
Ex:               int *iptr;        //pointer-to-int
                   float *fptr;    //pointer-to-float
                   char *cptr;    //pointer-to-char

The declaration int *iptr; does not mean that iptr is going to contain integer value.  What it means is, iptr is a pointer variable that is going to hold the address of an integer value. Hence, iptr is a pointer that points to integer value. In short, iptr is a pointer-to-int. 
          In the same way, the pointer fptr is going to contain the address of floating point value. Hence, fptr is a pointer that points to a floating point value. In short, fptr is a pointer-to-float.
Similarly, the pointer cptr is going to contain the address of a character. Hence, cptr is a pointer that points to a character. In short, cptr  is a pointer-to-char.

Initialization of a pointer variable: A pointer variable should be initialized with an address before it is used. The initialization of a pointer variable takes the following form:


Where Lvalue is any pointer variable and Rvalue is a variable preceded with an ampersand or a pointer of same type of LValue.


Ex:     int a=40;
          int *iptr1,*iptr2;       //Declaring pointer variables: iptr1,iptr2
          iptr1=&a;                //Initializing pointer variable: iptr1
          iptr2=iptr1;             //Initializing pointer variable: iptr2
Whenever a pointer variable is initialized with the address of an ordinary variable or a pointer variable, we can say that the pointer variable points to that ordinary variable or pointer variable. This can be represented as follows:



Accessing a pointer variable:

Once a pointer variable is initialized, it can be used to access the address as well as content of object to which it is pointed to. The following program demonstrates this:

Program to declare, initialize and access a pointer variable:

#include$ < $stdio.h$ > $
main()
{
          int a=40;
          int *iptr;        //Declaration: pointer-to-int
          iptr=&a;             //Initialization: with the address of a
         
          printf(“\n The address of a=%u”,&a);
          printf(“\n The address of a=%u”,iptr); //accessed to print address of a

          printf(“\n The value of a=%d”,a);
          printf(“\n The value of a=%d”,*(&a));
          printf(“\n The value of a=%d”,*iptr); //accessed to print the content of a
}

Operations on pointers:

Pointer arithmetic

The following arithmetic operations can be performed on pointers:
  • Addition of a number to a pointer.
  • Subtraction of a number from a pointer.
  • Subtraction of a pointer from another pointer.


The following arithmetic operations can not be performed on pointers:

×     Addition, multiplication, division and modulo division of two pointers.
×     Multiplication, division and modulo division of a pointer by a number.
×     Shifting operations.

Pointer increment and decrement:

Pointer Expression
Description
Ptr+n
Ptr=Ptr+n*sizeof(data_type_of_pointer)
Use the original value of ptr and then n*sizeof(data_type_of_pointer) is added to ptr after statement execution.
Ptr-n
Ptr=Ptr-n*sizeof(data_type_of_pointer)
Use the original value of ptr and then n*sizeof(data_type_of_pointer) is subtracted from ptr after statement execution.
Ptr++
Ptr=Ptr+ sizeof(data_type_of_pointer)
Use the original value of ptr and then ptr is incremented after statement execution.
Ptr--
Ptr=Ptr- sizeof(data_type_of_pointer)
Use the original value of ptr and then ptr is decremented after statement execution.
++Ptr
Ptr=Ptr+ sizeof(data_type_of_pointer)
Original Ptr is incremented before the execution of statement.
--Ptr
Ptr=Ptr- sizeof(data_type_of_pointer)
Original Ptr is decremented before the execution of statement.
*Ptr++
*(Ptr++)
Retrieve the content of the location pointed to by pointer and then increment ptr.
*Ptr--
*(Ptr--)
Retrieve the content of the location pointed to by pointer and then decrement ptr.
*++Ptr
*(++ptr)
Increment pointer and then retrieve the content of the new location pointed to by Ptr.
*--Ptr
*(--Ptr)
Decrement pointer and then retrieve the content of the new location pointer to by Ptr.
(*Ptr)++
Retrieve the content of *Ptr of the location pointed to by Ptr, and then Increment content of the location pointed to by Ptr. For pointer type content, use pointer arithmetic of standard arithmetic.
(*Ptr)--
Retrieve the content *Ptr of the location pointed to by Ptr, then decrement the content of that location; Ptr is not changed.

Pointer comparisons:

Two pointers can be compared by using relational operators as well as logical operators. When these operators encountered, these expressions may return either true or false. program to demonstrate pointer comparisons
#include$ < $stdio.h$ < $
main()
{
          int a=40,b=34;
          int *iptr1,*iptr2;      
          iptr1=&a;
               iptr2=&b;                   
          printf(“\n Equal condition of two pointers=%d”,(ip1==ip2));
printf(“\n Not Equal condition of two pointers=%d”,(ip1!=ip2));
printf(“\n Greater than condition of two pointers=%d”,(ip1>ip2));
printf(“\n Lesser than condition of two pointers=%d”,(ip1
printf(“\n Greater than or equals condition of two pointers=%d”,(ip1>=ip2));
printf(“\n Lesser than or equals condition of two pointers=%d”,(ip1<=ip2));
printf(“\n Logical AND condition of two pointers=%d”,( ip1>=ip2&&ip1==ip2));
printf(“\n Logical OR  condition of two pointers=%d”,(ip1==ip2|| ip1
printf(“\n Logical NOT condition of two pointers=%d”,(!(ip1==ip2)));
}

* Comparing two pointers those point to different types of objects lead to error, although they have addresses as data items in them.

Various pointer declarations:

Declare a variable of type float?
float x;
Declare an array that holds 10 floating-point values?
             float a[10];
Declare a function that takes a character as argument and returns a character as return value?
             char fun(char);
Declare a pointer-to-float?
            float *fptr;
Declare a function that returns a pointer-to-float?
          float * fun(void);
Declare a pointer-to-function that takes a double argument and returns a double value?
            double (*fun)(double);
Declare a pointer-to-10-integer element array?
            int (*arrptr)[10];
Declare an array of integer pointers of size 10?
            int *a[10];
Declare a pointer to char?
          char *cptr;
Declare a pointer-to-pointer to a char?
            char **cptr;
Declare a pointer-to-pointer-to-pointer to a char?
            char ***cptr;
Declare a function that returns a pointer to the array of floats?
          float (*fun())[10];
Declare an array of pointers to a function returning float?
            float (*f[10])();     

Problems with pointers: 

1) Segmentation fault (or segfault):

This generally means that a program tried to access the memory it shouldn’t have, invariably as a result of improper pointer use. The most likely causes could be inadvertent use of null pointers or uninitialized, misaligned, or otherwise improperly allocated pointers, corruption of malloc area and mismatched function arguments, especially involving pointers; two possible cases are scanf(“%d”,i) (without ampersand) and fprintf(invalid FILE* argument).

          A segmentation fault occurs when an attempt is made to access memory whose address is well-formed, but to which access can’t be granted. This might be due to either a protection fault or an invalid page fault.

2) Un-initialized pointer:

This generally occurs when we declared a pointer, forgot to initialize it with an address and tried to access the content through pointer. E.g.,
          int *ptr, val=300;
          *ptr=val;   /*Error*/         

3) When we want to assign a value to a pointer variable, then that assignment leads to an error. 
E.g.,      int *p, val=24;
                       p=val; /*Error*/ 

4) When we want to assign the address of un-initialized variable to a pointer, then that assignment also leads to an error.
E.g., int val,*ptr;
          ptr=&val;  /*Error*/

5) When we want to compare pointers those point to different objects, that comparison lead to an error
E.g.,   char name1[20],name2[20];
          char *p1=name1;
          char *p2=name2;
          if(p1>p2)….             /*Error*/