A Complete Guide to Learn Pointers in C

A beginner friendly blog which helps you to understand pointer in better way.

A Complete Guide to Learn Pointers in C

Introduction

This blog is written to address beginners learning C and 1st-year college students who are struggling with an important topic in C Programming called Pointers. But anyone can read this blog to learn something new. Pointer is a special topic and it only exists in C/C++.

Firstly, What is a Pointer? Pointers are special variables to point to a specific address in memory, its size is 8-bit. A pointer is mostly used to point to a variable, an array or an element of the array. We can also use it to point to a place where we allocated memory (dynamic memory allocation). Pointers can access the value of a variable and can also modify the value of that variable. Pointers are also variables so they also have an address and we can point to them also.

Some Basic Terminologies in Pointer

  1. Declaring a Pointer variable :

     int* intPtr;       //Pointer can point to a integer type variable
     char* charPtr;     //Pointer can point to a charater type variable
     void* ptr;         //Special type of pointer (we will learn about it after some time)
    

    It is a good practice to point the pointer to NULL when we declare it in our code, like this int* ptr = NULL; . Also, add a space between the pointer name and the type of pointer.

    Description for Declaration of Pointer

  2. Assigning value(address) to a Pointer

     //Method 1
     int num = 10;        //Sample variable
     int* ptr = NULL;     //Declaring a pointer
     ptr = #          //Pointer stores the address of that variable
    
     //Method 2
     int num = 10;        //Sample variable
     int* ptr = #     //Declaring and assigning address to a pointer
    

    Here we assign the address of the variable where the pointer should point. We use & operator to get the address of a variable. This is how it looks

    Visual Representation of what is happening in memory

  3. Accessing the Value where the Pointer points

     //In the above example
    
     //Accessing the value using "*" operator.
     printf("%d, %d \n",num,*ptr);    //Output : 10, 10
    
     //Changing value of num variable
     *ptr = 20;
     printf("%d, %d \n",num,*ptr);    //Output : 20, 20
    
     //Printing some values to give correct understanding
     printf("%d == %d \n",&num,ptr);            //Output : 3000 == 3000
     printf("%d->%d->%d \n",&ptr,ptr,*ptr);     //Output : 4000->3000->20
    

    We use * operator to access the value of the variable where the pointer points.

    Like in the above example, "ptr" points to "num" variable so, *ptr gives value 10. And when we do *ptr = 20; now num value is 20.

    Pro tip : *& and &* cancel each other. So, *&num , &*num and num give the same value which is 20.

  4. Double Pointer

    A double pointer is a pointer that points to a pointer. It stores the address of that pointer. Look at this code snippet

     //In the above example
    
     //Declaring a double pointer
     int** doublePtr = NULL;
     //Assigning it to (making it point to) pointer ptr
     doublePtr = &ptr;
    
     //Accessing value of num variable using
     printf("%d==%d==%d \n",**doublePtr,*ptr,num);    //Output : 20==20==20
     printf("%d, %d, %d, &d\n",&doublePtr,doublePtr,*doublePtr,**doublePtr);
     //Output : 4568, 4000, 3000, 20
    
     //Modifying the value of num variable
     **doublePtr = 10;
     printf("%d==%d==%d \n",**doublePtr,*ptr,num);    //Output : 10==10==10
    

    You can understand this using the following diagram of memory

Some code examples

Note : Sometimes using pointers gives Segmentation Fault (runtime error) which means you are using pointers in the wrong way, try to make a diagram of what is happening on paper for better clarity. You can also ask ChatGPT to point out where you are making the mistake.

Examples :

Swap Case

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

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

int main()
{
    int a = 10;
    int b = 20;
    printf("a = %d, b = %d\n",a,b);        //Output : a = 10, b = 20
    swap(a,b);
    printf("a = %d, b = %d\n",a,b);        //Output : a = 10, b = 20
    return 0;
}

As we can see here, values of a and b are not swapped here because the swap() function didn't swap these values in the main, when we send variables in the function, only their copy is sent not the variable itself so a,b in main are independent from a,b in swap function.

To fix this,

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

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

int main()
{
    int a = 10;
    int b = 20;
    printf("a = %d, b = %d\n",a,b);        //Output : a = 10, b = 20
    swap(&a,&b);                           //Sending address of a,b variables
    printf("a = %d, b = %d\n",a,b);        //Output : a = 20, b = 10
    return 0;
}

See this in the diagram given below

Using Double Pointers to change where Single Pointers Point

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

int main()
{
    int a = 10;
    int b = 20;
    int* ptr = &a;
    int** doublePtr = &ptr;
    printf("Before : %d\n",*ptr);        //Output : Before : 10

    //Now I will change where the single pointer ptr will point using doublePtr
    //"*doublePtr" access the ptr pointer and "**doublePtr" access the a variable
    *doublePtr = &b;
    printf("After : %d\n",*ptr);         //Output : After : 20
    return 0;
}

Here, doublePtr is the double-pointer which points to the single-pointer ptr. Firstly, ptr was pointing to the variable a. Lastly, ptr points to the variable b. How can we do this, one is we can directly change the single-pointer ptr to variable b using ptr = &b;, another method is using a double-pointer to change the single-pointer ptr as we can see this in this example. But why did we do that? Suppose that you can access the single-pointer ptr, to indirectly access that pointer we use a double-pointer. Explanation of the above example :

Making Dynamic Array and accessing its elements

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

//This function dynamically allocates array and return address of first element of the array
int* dynamicArray(int size)
{
    //It allocates memory for array of "size" elements
    int* newArray = (int*)malloc(size * sizeof(int));
    //If memory doesn't gets allocated so to do exception handling
    if (newArray == NULL)
    {
        return NULL;
    }
    return newArray;
}

int main()
{
    int* arr = dynamicArray(10);
    //arr[i] is same as *(arr+i)
    *(arr+2) = 10;
    printf("arr[2] : %d\n",*(arr+2));        //Output : arr[2] : 10
    return 0;
}

Here, dynamicArray() allocates memory for a dynamic array and returns the address of the first element of the array. And arr[i] == *(arr+i) == i[arr] works same.

We also have a function returning a pointer.

Null Pointer : Refer to https://www.geeksforgeeks.org/null-pointer-in-c/

Dangling Pointer : This is a pointer that got memory allocated but it got free after some time and now it’s pointing to the address where memory got deallocated. It could happen we allocate another variable and it get memory allocated at that same place as of dangling pointer and here dangling pointer will get access to the data which is owner by another variable (this is a security issue). See code as an example.

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

struct dog {
  int a;
  int b;
};

int main() {
    struct dog* x;
    struct dog* y;
    x = malloc(sizeof(struct dog));
    x->a = 10;
    free(x);        // Now x is a dangling pointer
    y = malloc(sizeof(struct dog));        // y got memory allocated at the same address as of x
    y->a = 11;
    printf("%d %d\n",y->a,x->a);
    // Output : 11 11
    // We freed x and allocated for y
    // Now, x is pointing to the same memory address of y.
}

See this interesting video related to this : https://youtu.be/lHSwkr623KI

Let's Solve some GATE questions

To practice questions go to https://www.geeksforgeeks.org/c-language-2-gq/pointers-gq/, below are some examples of how to solve these problems

Q1. What does the following program print? (GATE 2010)

#include <stdio.h>
void f(int *p, int *q)
{
  p = q;
 *p = 2;
}
int i = 0, j = 1;
int main()
{
  f(&i, &j);
  printf("%d %d \n", i, j);
  getchar();
  return 0;
}

Q2. Consider the C program shown below. The output of this program is ___. (GATE 2003)

#include <stdio.h>
#define print(x) printf("%d ", x)
int x;
void Q(int z)
{
    z += x;
    print(z);
}
void P(int *y)
{
    int x = *y + 2;
    Q(x);
    *y = x - 1;
    print(x);
}
main(void)
{
    x = 5;
    P(&x);
    print(x);
}

The answer to Q2. is 12 7 6

Q3. What is printed by the following C program? (GATE 2022)

#include<stdio.h> 
int main() 
{ 
   int x = 1, z[2] = {10, 11}; 
   int *p = NULL; 
   p = &x; 
   *p = 10; 
   p = &z[1]; 
   *(&z[0] + 1) += 3; 
   printf("%d, %d, %d\n", x, z[0], z[1]); 
   return 0; 
}

Q4. Consider the following function implemented in C. The output of the printxy(1,1) is ___. (GATE 2017)

void printxy(int x, int y)
{
    int *ptr;
    x = 0;
    ptr = &x;
    y = *ptr;
    *ptr = 1;
    printf("%d,%d", x, y);
}

The answer to Q4. is 1 0

Q5. Consider the following C program. The output of the program ___. (GATE 2016)

#include<stdio.h>
void mystery(int *ptra, int *ptrb) 
{
   int *temp;
   temp = ptrb;
   ptrb = ptra;
   ptra = temp;
}
int main() 
{
    int a=2016, b=0, c=4, d=42;
    mystery(&a, &b);
    if (a < c)
       mystery(&c, &a);
    mystery(&a, &d);
    printf("%d", a);
}

Ans. 5 The mystery() function also swaps where the ptra and ptrb are pointing, it doesn't change the variables in the main(). So, the mystery() function nothing has to do with the variables of main(). Hence, the value of variables doesn't change and a is still equal to 2016.

Some of my written programs to learn from (Self Learning Activity)

#include<stdio.h>

//For malloc function
#include<stdlib.h>

//This fuction takes input from user for number of rows and columns,
//Then Dynamically Allocate Memory to double pointer for making 2D Array,
//This fuction also takes input for elements of 2D Array from user,
//And in last, return the double pointer which is the 2D Array.
int**readMatrix(int*row,int*column)
{
    //Double Pointer for 2D Array.
    int**t;
    int i,j;

    //For Exception Handling for negative number for number of rows and columns.
    do
    {
    printf("Enter number of rows and columns: ");
    scanf("%d %d",row,column);
    if(*row<=0 || *column<=0)
        printf("Number of Rows and Columns are always positive numbers.\n");
    }
    while(*row<=0 || *column<=0);

    //Allocating Memory for array of pointers
    t=(int**)malloc(*row * sizeof(int*));

    //Taking Input of Elements of 2D Array from user.
    printf("Enter values of array:\n");
    for(i=0;i<*row;i++)
    {
        t[i] = (int*)malloc((*column)*sizeof(int));
        for(j=0;j<*column;j++)
        {
            scanf("%d",&t[i][j]);
        }
    }
    printf("\n");

    //Returning the 2D Array as double pointer.
    return t;
}

//This function is for checking 2D Array(Matrix) is upper triangle or not.
//1 for true and 0 for false.
int isTriangular(int**arr,int row,int column)
{
    //If number of rows and columns are not equal so not a upper triangle.
    if(row!=column)
        return 0;

    //Checking the upper triangle after number of rows and columns are equal.
    int i,j;
    for(i=1;i<row;i++)
    {
            for(j=0;j<i;j++)
            {
                    if(arr[i][j]!=0)
                    {
                        return 0;
                    }
            }
    }
    return 1;
}

//This first checks if Matrix(2D Array) is upper triangle or not.
//If false, return 0.
//If true, return 1 and using pointers give sum of diagonals and sum of all elements.
int sumTriangular(int**arr,int row,int column,int*sum_diagonal,int*sum_all)
{
    int i,j;
    *sum_diagonal=0;
    *sum_all=0;
    if(isTriangular(arr,row,column)==0)
    {
        return 0;
    }
    else
    {
        for(i=0;i<row;i++)
        {
            for(j=0;j<column;j++)
            {
                if(i==j)
                {
                    *sum_diagonal=*sum_diagonal+arr[i][j];
                }
            }
        }
        for(i=0;i<row;i++)
        {
            for(j=0;j<column;j++)
            {
                *sum_all=*sum_all+*(*(arr+i)+j);
            }
        }
    }
    return 1;
}

//This is main function
int main()
{
    //Double pointer for 2D Array(Matrix).
    int**arr;

    int row,column,sum_diagonal,sum_all;
    int i,j;

    //Calling readMatrix function and saving returned 2D Array in arr double pointer.
    arr=readMatrix(&row,&column);

   //Printing the 2D Array(Matrix).
    printf("\nDisplay The array :\n");
    for(i=0;i<row;i++)
    {
        for(j=0;j<column;j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
    printf("\n");

   //At last, calling function for upper triangle.
    if(sumTriangular(arr,row,column,&sum_diagonal,&sum_all)==0)
    {
        printf("The matrix is not an upper triangular matrix");
    }
    else
    {
        printf("The sum of the diagonal elements is: %d\n\nThe sum of all elements of upper triangular matrix is: %d\n",sum_diagonal,sum_all);
    }
    return 0;
}
/*
    Dynamically Allocate Memory using function and double pointer for making Matriz with different number of columns in different rows.
*/

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

int *read(int **arr, int *row)
{
    int i, j;
    int *column;

    column = (int *)malloc(*row * sizeof(int));

    for (i = 0; i < *row; i++)
    {
        printf("Enter Number of Columns of %dth row : ", i);
        scanf("%d", &column[i]);
        *(arr + i) = (int *)malloc(column[i] * sizeof(int));

        for (j = 0; j < column[i]; j++)
        {
            printf("Enter matrix[%d][%d] : ", i, j);
            scanf("%d", &arr[i][j]);
        }
    }

    return column;
}

int main()
{
    int **matrix;
    int *column;
    int row;
    int i, j;

    printf("Enter Number of Rows of Matrix : ");
    scanf("%d", &row);
    matrix = (int **)malloc(row * sizeof(int *));

    column = read(matrix, &row);

    printf("\nNow Displaying the Matrix :\n");
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < column[i]; j++)
        {
            printf(" %d ", matrix[i][j]);
        }
        printf("\n");
    }
    return 0;
}

If you find something difficult to understand, you can use ChatGPT and ask it to help/explain it to you.

A meme before concluding

Source

Conclusion

I hope you find this blog helpful and learned something new from it. Please share it with your friends who find the pointer topic hard or confusing. Pointer is a very long topic and it cannot be covered in this blog. You can now try pointers in your C programs, and please look into some more GATE questions to learn some uncovered things and strengthen this topic. You can contact me on Twitter : https://twitter.com/shreyansh_2022 and check out my other blogs.

Thanks for Reading 🙌

Did you find this article valuable?

Support Shreyansh Thoughts by becoming a sponsor. Any amount is appreciated!