× back About pointer Syntax of pointer Operator in pointer Pointer initializaiton Dereferencing a pointer Memory and pointer Storing one pointer value to another pointer value. Pointer to pointer Pointer to function Pointer arithmetic Pointer and 1D array Types of pointers Pass by value and reference
Next Topic → ← Previous Topic

Pointer

Advantages of pointer:

  1. Dynamic memory allocation: Pointers allow dynamic memory allocation, which means that memory can be allocated during runtime, depending on the program's needs. This makes it easier to create data structures like linked lists, trees, etc.
  2. Pass by reference : C uses pass by value as a default, which means that function arguments are copied to the function's stack. With pointers, it is possible to pass a variable by reference, which means that the function can modify the original variable.
  3. Efficient memory usage: Pointers allow efficient use of memory since they can point to any memory location. This can help to reduce memory usage, especially for large data structures.

Syntax of pointer

                             
datatype *pointer_name;

int *p;
float *f;
char *c;
                   
               

Operator in pointer

Pointer initialization

C pointer definition

                       
datatype *pointer_name = address;
                       
                   
  • The above method is called pointer definition as the pointer is declared and initialized at the same time.

Initialization after declaration

  • The second method of pointer initialization in C is assigning some address after the declaration.
                       
datatype *pointer_name;
pointer_name = address;
                       
                   

Dereferencing a C pointer

Program to calculate sum of two number using pointer.

                       
#include<stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    int c = 0;
    int *x = &a;
    int *y = &b;
    int *z = &c;
    *z = *x + *y;
    printf("The sum is %d", *z);
    return 0;
}
                       
                   
                
#include <stdio.h>
int main()
{
int var = 10, *ptr;
ptr = &var;

printf("Value at ptr = %p\n", ptr);         // prints 0x7ff7bec385a8
printf("Value at var = %d\n", var);         // prints 10
printf("Value at *ptr = %d\n", *ptr);       // prints 10
printf("Value at *(&var) = %d\n", *(&var)); // prints 10
printf("Value at &ptr = %p\n", &ptr); // prints address of ptr variable - 0x7ff7b1dc55a0
return 0;
}

/*
%p is used for printing address in Hexa decimal form
%u is unsigned form
*/
                
            

Memory and pointer

                   
#include <stdio.h>
int main()
{
    int a = 5, *p1;
    float b = 2.5, *p2;
    char c = 'a', *p3;
    p1 = &a;
    p2 = &b;
    p3 = &c;
    printf("%d", sizeof(p1)); // 8
    printf("%d", sizeof(p2)); // 8
    printf("%d", sizeof(p3)); // 8 - in my system pointer size is 8 bytes
    return 0;
}
                   
               

Storing one pointer value to another pointer value.

                   
#include<stdio.h>
int main()
{
    int var = 10;
    int *ptr;
    int *q;
    ptr = &var;
    // *q = *ptr; // this is invalid 
    // because (q) is not initialized with any address so we cannot dereference it.
    return 0;
}
                   
               
                   
#include <stdio.h>
int main()
{
    int var = 10, var2 = 20;
    int *ptr;
    int *q;
    ptr = &var;
    q = &var2;
    *q = *ptr; // now this is valid
    printf("%d", *q);
    return 0;
}
                   
               

Pointer to Pointer (Double Pointer)

Program to demonstrate use of double pointer.

                       
#include<stdio.h>
int main()
{
    // int *p = &a, a =10; // this statement is wrong because "a" have to be declared first.
    int a = 10, *p, **q;
    p = &a;
    q = &p;

    printf("%d", a); // 10
    printf("%d", *p); // 10
    printf("%d", **q); // 10
    return 0;
}
                       
                   

Pointer to function

To declare a pointer to a function in C, you use the following syntax:

                   
return_type (*pointer_name)(argument_list);
                   
               

For example, here's a declaration of a function pointer that takes two integers and returns an integer ↓

                   

int (*add)(int, int);
                   
               

To assign a function to a function pointer, you simply assign the address of the function to the pointer variable ↓

                                      
int sum(int a, int b)
{
    return a + b;
}

add = &sum;
                   
               

To call the function through the pointer, you can use the following syntax ↓

                   
int result = (*add)(3, 4);
                   
               

Alternatively, you can use the shorthand notation ↓

                                      
int result = add(3, 4);
                   
               

Here's a complete example program that demonstrates the use of function pointers in C ↓

                   
#include <stdio.h>

int sum(int a, int b) {
    return a + b;
}

int product(int a, int b) {
    return a * b;
}

int main() {
    int (*func_ptr)(int, int);
    
    func_ptr = &sum;
    printf("sum(3, 4) = %d\n", (*func_ptr)(3, 4));
    
    func_ptr = &product;
    printf("product(3, 4) = %d\n", (*func_ptr)(3, 4));
    
    return 0;
}                        
                   
               

Pointer Arithmetic

Addition of an integer to a pointer and increment operation.

                       
int *ptr;
// let's say address of "ptr" is 1000
ptr = ptr + 1;
// let's say integer takes 4 bytes then
ptr + 1 will be 1004

// now same with increment 
p++ means 1004 + 4 = 1008
                       
                   

Program to show pointer arithmetic ↓

                   
#include <stdio.h>
int main()
{
    int a = 5, *pi = &a;
    char b = 'x', *pc = &b;
    float c = 5.5, *pf = &c;
    printf("Value of pi = %d  Address of a = %lu\n", *pi, (unsigned long)pi);
    printf("Value of pc = %c  Address of b = %lu\n", *pc, (unsigned long)pc);
    printf("Value of pf = %.1f  Address of c = %lu\n", *pf, (unsigned long)pf);

    // Incrementing pointers using pointer arithmetic
    pi++;
    pc++;
    pf++;

    printf("After incrementing:\n");
    printf("Modified address of a = %lu\n", (unsigned long)pi);
    printf("Modified address of b = %lu\n", (unsigned long)pc);
    printf("Modified address of c = %lu\n", (unsigned long)pf);

    // Decrementing pointers using pointer arithmetic
    pi--;
    pc--;
    pf--;

    printf("After decrementing:\n");
    printf("Modified address of a = %lu\n", (unsigned long)pi);
    printf("Modified address of b = %lu\n", (unsigned long)pc);
    printf("Modified address of c = %lu\n", (unsigned long)pf);

    // Adding a constant value to pointers
    pi += 2;
    pc += 2;
    pf += 2;

    printf("After adding 2:\n");
    printf("Modified address of a = %lu\n", (unsigned long)pi);
    printf("Modified address of b = %lu\n", (unsigned long)pc);
    printf("Modified address of c = %lu\n", (unsigned long)pf);

    // Subtracting a constant value from pointers
    pi -= 2;
    pc -= 2;
    pf -= 2;

    printf("After subtracting 2:\n");
    printf("Modified address of a = %lu\n", (unsigned long)pi);
    printf("Modified address of b = %lu\n", (unsigned long)pc);
    printf("Modified address of c = %lu\n", (unsigned long)pf);

    return 0;
}
                   
               

Output ↓

                   
Value of pi = 5  Address of a = 140701867095480
Value of pc = x  Address of b = 140701867095471
Value of pf = 5.5  Address of c = 140701867095452
After incrementing:
Modified address of a = 140701867095484
Modified address of b = 140701867095472
Modified address of c = 140701867095456
After decrementing:
Modified address of a = 140701867095480
Modified address of b = 140701867095471
Modified address of c = 140701867095452
After adding 2:
Modified address of a = 140701867095488
Modified address of b = 140701867095473
Modified address of c = 140701867095460
After subtracting 2:
Modified address of a = 140701867095480
Modified address of b = 140701867095471
Modified address of c = 140701867095452
                   
               

Precedence of dereferencing operator and increment/decrement operators

  • The precedence level of * operator and incremenet/decrement operators is same and their associativity is from right to left.
  • There can be confusion while combining these operators in pointer expressions, so we should use them carefully.

x = *ptr++;

  • The expression *ptr++ is equivalent to *(ptr++).
  • Since these operators associate from right to left. Hence the increment operator will be applied to ptr, and not to *ptr.
  • The increment operator is postfix, so first the value of ptr will be used in the expression and then it will be incremented.
  • Hence firstly the integer pointed to by ptr will be dereferenced and assigned to x and then ptr will be incremented.
  • This is same as:
                                                   
    x = *ptr;
    ptr = ptr + 1;
                                       
                                   

x = *++ptr;

  • The expression *++ptr is equivalent to *(++ptr).
  • Here also the increment operator is applied ptr.
  • The increment operator is prefix, so first ptr will be incremented and then its new value will be used in the expression.
  • Hence firstly the value of ptr is incremented, then value at the new address is dereferenced and assigned to x.
  • This is same as:
                                                   
    *ptr = *ptr + 1;
    x = *ptr;
                                       
                                   

x = ++*ptr

  • The expression ++*ptr is equivalent to ++(*ptr).
  • Here the increment operator is applied over *ptr and not ptr.
  • So ere the value of pointer will not change but the value pointed to by the pointer will change i.e. *ptr will increment.
  • Since the increment operator is prefic hence first the value of *ptr will increment and then this will be assigned to x.
  • This is same as:
                                                   
    *ptr = *ptr + 1;
    x = *ptr;
                                       
                                   

x = (*ptr)++

  • Here also the increment operator is applied over *ptr and since it is postfix increment hence first the value of *ptr will be assigned to x and then it will be incremented.
  • This is same as:
                                                   
    x = *ptr;
    *ptr = *ptr + 1;
                                       
                                   

Let us take an example and understad how these expressions are interpreted.

  • Suppose value at address 2000 is 25, value at address 2004 is 38, ptr is an integer (4 byte size) pointer that contains address 2000 hence value of *ptr is 25.
  • now we'll see what will be the result in these cases:
    1. x = *ptr++;
    2. x = *++ptr;
    3. x = ++*ptr;
    4. x = (*ptr)++;

Pointers and One Dimensional Arrays

Following program shows that the elements of an array are stored in consecutive memory locations.

                                 
#include <stdio.h>
int main()
{
int arr[5] = {5, 10, 15, 20, 25};
int i;
for (i = 0; i < 5; i++)
{
    printf("Value of arr[%d] = %d\t", i, arr[i]);
    printf("Address of arr[%d] = %u\n", i, &arr[i]);
}
return 0;
}
                   
               

Output ↓

                   
Value of arr[0] = 5     Address of arr[0] = 2000
Value of arr[1] = 10    Address of arr[1] = 2002
Value of arr[2] = 15    Address of arr[2] = 2004
Value of arr[3] = 20    Address of arr[3] = 2006
Value of arr[4] = 25    Address of arr[4] = 2008
                   
               
                   
arr   -> Points to 0th element -> &arr[0] -> 2000
arr+1 -> Points to 1st element -> &arr[1] -> 2002 (assuming 'int' take 2 bytes)
arr+2 -> Points t0 2nd element -> &arr[2] -> 2004
arr+3 -> Points to 3rd element -> &arr[3] -> 2006
arr+4 -> Points to 4th element -> &arr[4] -> 2008
                   
               
                   
*arr      ->   Value of 0th element   -> arr[0]   -> 5
*(arr+1)  ->   Value of 1st element   -> arr[1]   -> 10
*(arr+2)  ->   Value of 2nd element   -> arr[2]   -> 15
*(arr+3)  ->   Value of 3rd element   -> arr[3]   -> 20
*(arr+4)  ->   Value of 4th element   -> arr[4]   -> 25
                   
               

Program to print the value and address of elements of an array using pointer notation

                   
#include <stdio.h>
int main()
{
    int arr[5] = {5, 10, 15, 20, 25};
    int i;
    for (i = 0; i < 5; i++)
    {
        printf("Value of arr[%d] = %d\t", i, *(arr + i));
        printf("Address of arr[%d] = %u\n", i, arr + i);
    }
    return 0;
}
                   
               

Output ↓

                   
Value of arr[0] = 5     Address of arr[0] = 2000
Value of arr[1] = 10    Address of arr[1] = 2002
Value of arr[2] = 15    Address of arr[2] = 2004
Value of arr[3] = 20    Address of arr[3] = 2006
Value of arr[4] = 25    Address of arr[4] = 2008
                   
               

Types of Pointers

Null Pointer

  • When we assign null value at the time of pointer declaration.
  • This method is useful when you do not assign any address to the pointer. A null pointer always contains value 0.

Example code ↓

                       
#include <stdio.h>
int main()
{
    int *ptr = NULL; // null pointer
    printf("The value inside variable ptr is:%d",ptr);
    return 0;
}
                       
                   

Output ↓

                       
The value inside variable ptr is:0
                       
                   

Void Pointer

  • It is a pointer that has no associated data type with it.
  • A void pointer can hold address of any type and can be typecast to any type.
  • It is also called a generic pointer and does not have any standard data type.
  • It is created by using the keyword void.

Example code ↓

                       
#include <stdio.h>
int main()
{
    char a = 'a';
    void *p; // void pointer
    p = &a;
    printf("The size of pointer is:%d", sizeof(p)); // size of p depends on compiler
    return 0;
}
                       
                   

Output ↓

                       
The size of pointer is:8
                       
                   
  • Type casting is used here to use void pointer.
                       
#include <stdio.h>
int main()
{
    int a = 10;
    void *ptr = &a;
    printf("*ptr = %d", *(int *)ptr);
    return 0;
}
                       
                   

Wild Pointer

  • Wild pointers are also called unintialized pointers.
  • Because they point to some arbitrary memory location and may cause a program to crash or behave badly.
  • This type of C pointer is not efficient. Because they may point to some unknown memory location which may cause problems in our program. This may lead to the crashing of the program.
  • Advised to be cautious while working with wild pointers.

Example code ↓

                       
#include <stdio.h>
int main()
{
    int *p; // wild pointer
    printf("%d", *p);
    return 0;
}
                       
                   

Output ↓

                       
Segment fault
                       
                   

Dangling Pointer

  • A pointer pointing to a memory location that has been deleted (or freed) is called dangling pointer.
  • There are three different ways where pointer acts as dangling pointer.

1. De-allocation of memory

                               
#include<stdio.h>
#include<stdlib.h>

int main()
{
    int *ptr = (int *)malloc(sizeof(int));
    
    // after free call, ptr becomes a dangling pointer 
    free(ptr);

    ptr = NULL; // no more dangling pointer
    return 0;
}
                           
                       

2. Function call

                               
#include<stdio.h> 

int * fun() // function returns address
{
    // x is local variable and goes out of scope after an execution of fun() is over.
    int x = 5;

    return &x;
}

int main()
{
    int *p = fun();

    // p points to something which is not valid anymore 
    printf("%d", *p);
    return 0;
}
                           
                       
  • For the above program "p" doesn't become dangling if x is a static variable.

3. Variable goes out of scope

                               
void main()
{
    int *ptr;
    ... 
    ... 
    {
        int ch;
        ptr = &ch;
    }
    ...
    // here ptr is dangling pointer.
}
                           
                       

Near pointer

  • Before this we should know about residence memory.
  • We should know that every address have two parts:
    1. Segment address (2 bytes)
    2. Offset address (2 bytes)
  • Near pointer only store offset address. That's why it's size is 2 bytes.
  • It can store the address of maximum 16 bits only.
  • We can only access 64b of data at a time. Thus, it is considered one of the major disadvantages of the near pointer, which is why it is no longer commonly used.
  • It is a pointer that works within the range of the 64Kb data segment of memory.
  • It cannot access addresses beyond the given data segment.
  • A near pointer can be incremented or decremented in the address range by using an arithmetic operator

Example ↓

                               
char near * str;
int near * a;
                           
                    

Far pointer

  • It is 32 bit pointer (4 bytes).
  • A far pointer is a pointer that can access memory beyond the 64 KB segment boundaries.
  • It is used to point to variables that are located in a different segment than the pointer itself.

Example ↓

                               
int far * ptr;
                           
                    

Huge pointer

  • It is a 32 bit pointer (4 byte).
  • A huge pointer is a pointer that can access memory beyond the 64 KB segment boundaries but has a larger segment size than a far pointer.
  • Huge pointer can access whole residence memory.

Example ↓

                               
char huge * ptr;
                           
                       

Pass by value and pass by reference

Pass by value

  • Pass by value (another term is 'call by value') means the code within a function cannot alter the arguments used to call the function, even if the values are changed inside the function.
  • That means C creates a copy of the data that the function uses.

An example of a 'swap' function to demonstrate pass by value ↓

                       
#include <stdio.h>
void swap(int x, int y)
{
    int temp = x;
    x = y;
    y = temp;
}
int main()
{
    int a = 50;
    int b = 100;
    printf("\nBefore Swap : \n a = %d \n b = %d", a, b);
    swap(a, b);
    printf("\n\nAfter Swap : \n a = %d \n b = %d\n", a, b);
    return 0;
}
                       
                   

output ↓

                       
Before Swap : 
a = 50 
b = 100

After Swap : 
a = 50 
b = 100
                       
                   
  • In the above code the value of 'a' and 'b' remains the same.

Pass by reference

  • Passing arguments to a called function by reference, means passing the address of memory location of the data to the function using '&' (addressof) operator.
  • Here what is passed in is a copy of the pointer, but what it points to is still the same address in memory as the original pointer, so this allows the function to change the value outside the function.

Swap function using pass by reference ↓

                       
#include <stdio.h>
void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
int main()
{
    int a = 50;
    int b = 100;
    printf("\nBefore Swap : \n a = %d \n b = %d", a, b);
    swap(&a, &b);
    printf("\n\nAfter Swap : \n a = %d \n b = %d\n", a, b);
    return 0;
}
                       
                   

Output ↓

                       
Before Swap : 
a = 50 
b = 100

After Swap : 
a = 100 
b = 50
                       
                   

Program to sort an array using pointer ↓

                       
#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void sort(int *arr, int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = i+1; j < n; j++) {
            if (*(arr+j) < *(arr+i)) {
                swap(arr+i, arr+j);
            }
        }
    }
}

void printArray(int *arr, int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", *(arr+i));
    }
    printf("\n");
}

int main() {
    int arr[] = {5, 3, 7, 1, 8};
    int n = sizeof(arr) / sizeof(int);

    printf("Original array: ");
    printArray(arr, n);

    sort(arr, n);

    printf("Sorted array: ");
    printArray(arr, n);

    return 0;
}
                       
                   

References ↓