The significance of pointers in C is the flexibility it offers in the programming. Pointers enable us to achieve parameter passing by reference, deal concisely and effectively either arrays, represent complex data structures, and work with dynamically allocated memory.

Although a lot of programming can be done without the use of pointers, their usage enhances the capability of the language to manipulate data. Pointers are also used for accessing array elements, passing arrays and strings to functions, creating data structures such as linked lists, trees, graphs, and so on.

What is a Pointer Variable

Memory can be visualized as an ordered sequence of consecutively numbered storage locations. A data item is stored in memory in one or more adjacent storage locations depending upon its type. The address of a data item is the address of its first storage location. This address can be stored in another data item and manipulated in a program. The address of a data item is called a pointer to the data item and a variable that holds an address is called a pointer variable.

Uses of Pointers

  1. Keep track of address of memory locations.
  2. By changing the address in pointer type variable you can manipulate data in different memory locations.
  3. Allocation of memory can be done dynamically.

Address and differencing (& AND *) Operators

Consider the declaration:

The compiler will automatically assign memory for this data item. The data item can be accessed if we know the location (i.e., the address) of the first memory cell.

c addressing and differencing operators

The address of num’s memory location can be determined by the expression &num, where & is unary operator, called the ‘address of’ operator. It evaluates the address of its operand. We can assign the address of num to another variable, pnum as:

This new variable pnum is called a pointer to num, since it points to the location where num is stored in memory. Thus pnum is referred to as a pointer variable. The data item represented by num, can be accessed by the expression *pnum, where * is unary operator, called ‘the value at the address’ operator. It operates only on a pointer variable.

It can be illustrated as below:

c pointers basics

Relationship between pnum and num (where pnum = &num and num = *pnum). Therefore, *pnum and num both represent the same data item. Accessing a data item through a pointer is called Dereferencing, and the operator asterisk (*) is called the dereferencing or indirection operator.

Pointer Type Declaration

Pointers are also variables and hence, must be defined in a program like any other variable. The rules for declaring pointer variable names are the same as ordinary variables.

The declaration of a pointer is of the following form:

where,
type: Data type of the variable pointed by the pointer variable.
variable_name: Name of the pointer variable
*(asterisk): Signifies to the compiler that this variable has to be considered a pointer to the data type indicated by type.

For example,

int *int_ptr       ### int_ptr is a pointer to data of type integer
char *ch_ptr       ### ch_ptr is a pointer to data of type character
double *db_ptr     ### db_ptr is a pointer to data of type double

Note: The size of any pointer in C is same as the size of an unsigned integer. Hence it is Architecture dependent.

Pointer Assignment

The addressof (&) operator, when used as a prefix to the variable name, gives the address of that variable.

Thus,

assigns address of variable i to ptr.

/* Example of ‘&’ - address of operator */ 
#include <stdio.h>
void main(void) 
{ 
    int a=100; 
    int b=200; 
    int c=300; 
    printf(“Address:%u contains value :%dn”, &a, a); 
    printf(“Address:%u contains value :%dn”, &b, b); 
    printf(“Address:%u contains value :%dn”, &c, c); 
}

Output:

Address:65524 contains value :100 
Address:65520 contains value :200 
Address:65516 contains value :300

A pointer value may be assigned to another pointer of the same type.

For example, in the program below:

int i=1, j, *ip; 
ip=&i; 
j=*ip; 
*ip=0;

The first assignment assigns the address of variable i to ip. The second assigns the value at address ip, that is, 1 to j, and finally to the third assigns 0 to i since *ip is the same as i.

The two statements

are equivalent to the single assignment

or to the assignment

i.e., the address of operator & is the inverse of the dereferencing operator *.

Consider the following segment of code:

#include <stdio.h>
void main(void) 
{ 
    char *ch; 
    char b = ’A’; 
    ch = &b; /* assign address of b to ch */ 
    printf(“%c”, *ch); 
}

Memory representation of pointer

In the above example,
b: value of b, which is ‘A’
&b: address of b, i.e., 36624
ch: value of ch, which is 36624
&ch: address of ch, i.e., 4020 (arbitrary)
*ch: contents of ch, => value at 36624, i.e. A. This is same as *(&b)

Pointer Initialization

The declaration of a pointer variable may be accompanied by an initializer. The form of an initialization of a pointer variable is:

type *identifier=initializer; 

The initializer must either evaluate to an address of previously defined data of appropriate type or it can be NULL pointer. For example, the declaration initializes fp with a null value.

The declarations

char c[10]; 
char *cp=&c[4]; 

initialize cp to the address of the fifth element of the array c.

initialize cfp to the address of the first element of the array c. It can also be written as:

Address of first element of an array is also called as base address of array. Following program illustrates declaration, initialization, assignment and dereferencing of pointers.

/* Example : Usage of Pointers */ 
# include <stdio.h>
void main(void) 
{ 
    int i, j=1; 
    int *jp1, *jp2=&j; /* jp2 points to j */ 
    jp1 = jp2; /* jp1 also points to j */ 
    i = *jp1; /* i gets the value of j */ 
    *jp2 = *jp1 + i; /* i is added to j */ 
    printf(“i=%d j=%d *jp1=%d *jp2=%dn”, i, j, *jp1, *jp2); 
}

Output:

Pointer Arithmetic

Arithmetic can be performed on pointers. However, in pointer arithmetic, a pointer is a valid operand only for the addition(+) and subtraction(-) operators. An integral value n may be added to or subtracted from a pointer ptr. Assuming that the data item that ptr points to lies within an array of such data items. The result is a pointer to the data item that lays n data items after or before the one ptr points to respectively.

The value of ptr±n is the storage location ptr±n*sizeof(*ptr), where sizeof is an operator that yields the size in bytes of its operand. Consider following example:

#include <stdio.h>
void main(void) 
{ 
    int i=3, *x; 
    float j=1.5, *y; 
    char k=’C’, *z; 
    printf(“Value of i=%dn”, i); 
    printf(“Value of j=%fn”, j); 
    printf(“Value of k=%cn”, k); 
    x=&i; 
    y=&j; 
    z=&k; 
    printf(“Original Value in x=%un”, x); 
    printf(“Original Value in y=%un”, y); 
    printf(“Original Value in z=%un”, z); 
    x++; 
    y++; 
    z++; 
    printf(“New Value in x=%un”, x); 
    printf(“New Value in y=%un”, y); 
    printf(“New Value in z=%un”, z); 
 }

Output:

Value of i=3 
Value of j=1.500000 
Value of k=C 
Original Value in x=1002 
Original Value in y=2004 
Original Value in z=5006 
New Value in x=1006 
New Value in y=2008 
New Value in z=5007

In the above example, New value in x is 1002(original value)+4, New value in y is 2004(original value)+4, New value in z is 5006(original value)+1.

This happens because every time a pointer is incremented it points to the immediately next location of its type. That is why, when the integer pointer x is incremented, it points to an address four locations after the current location, since an int is always 4 bytes long. Similarly, y points to an address 4 locations after the current locations and z points 1 location after the current location.

Some valid pointer arithmetics are as shown below:

Addition of a number to a pointer

For example, we can write

int *ip;
int a[10];
ip = &a[3];

and we would end up with ip pointing at the fourth cell of the array a (remember, arrays are 0-based, so a[0] is the first cell). We could illustrate the situation like this:

Addition of a number to a pointer in C

We’d use this ip just like the one in the previous section: *ip gives us what ip points to, which in this case will be the value in a[3]. Once we have a pointer pointing into an array, we can start doing pointer arithmetic. Given that ip is a pointer to a[3], we can add 1 to ip:

What does it mean to add one to a pointer? In C, it gives a pointer to the cell one farther on, which in this case is a[4]. To make this clear, let’s assign this new pointer to another pointer variable:

Now the picture looks like this:

C pointers

If we now do

we’ve set a[4] to 4.

Subtraction of a number from a pointer

We can also compute ptr – i. For example, suppose we have an int array called arr.

int arr[ 10 ] ; 
int * p1, * p2 ; 
 
p1 = arr + 3 ; // p1 == & arr[ 3 ] 
p2 = p1 - 2 ; // p1 == & arr[ 1 ]

This is because when a pointer is decremented (or incremented) it is done so by the length of the data type it points to, called the scale factor.

Some invalid pointer arithmetics are:

  • Addition two pointers.
  • Multiplication of a number with a pointer.
  • Division of a pointer with a number.

Pointer Comparison

The relational comparisons ==,!= are permitted between pointers of the same type. The relational comparisons <, <=, >, >= are permitted between pointers of the same type and pointing to the same type. The result depends on the relative location of the two data items pointed to.

For example,

the expression

is true if ap is pointing to the last element of the array a, and the expression

is true as long as ap is pointing to one of the elements of a.

Pointers and Functions

A function can take a pointer to any data type, as argument and can return a pointer to any data type. For example, the function definition

double *maxp(double *xp, double *yp) 
{ 
    return *xp >= *yp ? x; 
}

specifies that the function maxp() return a pointer to a double variable, and expects two arguments, both of which are pointers to double variables. The function de-references the two argument pointers to get the values of the corresponding variables, and returns the pointer to the variable that has the larger of the two values. Thus given that,

the statement

makes mp point to v.

call by value

In a call by value, values of the arguments are used to initialize parameters of the called function, but the addresses of the arguments are not provided to the called function. Therefore, any change in the value of a parameter in the called function is not reflected in the variable supplied as argument in the calling function.

/* Example: Function parameters passed by Value */ 
#include  
void main(void) 
{ 
   int a=5, b=7; 
   void swap(int, int); 
   printf(“Before function call: a=%d b=%d”, a, b); 
   swap(a, b); /* Variables a and b are passed by value */ 
   printf(“After function call: a=%d b=%d”, a, b); 
} 
void swap(int x, int y) 
{ 
   int temp; 
   temp=x; 
   x=y; 
   y=temp; 
}

Output:

Before function call: a=5 b=7 
After function call: a=5 b=7

Call by reference

In contrast, in a call by reference, addresses of the variables are supplied to the called function and changes to the parameter values in the called function cause changes in the values of the variable in the calling function.

Call by reference can be implemented by passing pointers to the variables as arguments to the function. These pointers can then be used by the called function to access the argument variables and change them.

/* Example : Arguments as pointers */ 
#include  
void main(void)
{ 
   int a=5, b=7; 
   void swap(int*, int*); 
   printf(“Before function call: a=%d b=%d”, a, b); 
   swap(&a, &b); /* Address of variable a and b is passed */ 
   printf(“After function call: a=%d b=%d”, a, b); 
} 
void swap(int *x, int *y) 
{ 
    int temp; 
    /* The contents of memory location are changed */
    temp=*x; 
    *x=*y; 
    *y=temp; 
}

Output:

Before function call: a=5 b=7 
After function call: a=7 b=5 

Steps involved for using pointers in a function are

  1. Pass address of the variable (Using the ampersand (&) or direct pointer variables).
  2. Declare the variable as pointers within the routine.
  3. Refer to the values contained in a memory location via asterisk (*).

Using call by reference, we can make a function return more than one value at a time, as shown in the program below:

/* Returning more than one values from a function through arguments */ 
# include <stdio.h>
void main(void) 
{ 
    float radius; 
    float area, peri; 
    void areaperi(float, float*, float*); 
    printf("Enter radius : "); 
    scanf("%f", &radius); 
    areaperi(radius, &area, &peri); 
    printf("nArea = %.2f n", area); 
    printf("Perimeter = %.2f", peri); 
} 
void areaperi(float r, float *a, float *p) 
{ 
    *a = 3.14 * r * r; 
    *p = 2 * 3.14 * r; 
}

Output:

Enter radius of a circle : 5 
Area=78.50 
Perimeter=31.40