Open In App

Common Memory/Pointer Related bug in C Programs

Dereferencing an unknown memory location : C programmers mostly use scanf() function to take input, but sometimes a small mistake can bring a bug or even crash whole program.
The syntax for scanf() is scanf(“%d”, &a);. It might be possible to miss a & and write &a as a so now scanf(“%d”, a); is dereferencing to an unknown location.
Now either the program may terminate with an exception or it may correspond to a valid location(not related to current program but to some other program) and may get overwritten which may cause unknown effect later.




// A C program to demonstrate that missing
// an & in scanf() may cause memory problems.
#include <stdio.h>
  
int main()
{
    int a;
    scanf("%d", a);
    printf("%d", a);
    return 0;
}

Reading an uninitialized Memory. In C, beginners generally use malloc() to provide run time memory but with malloc() the memory block do not get initialized and one may access .




// A C program to demonstrate that missing
// an & may cause memory problems.
#include <stdio.h>
  
int main()
{
    int* p = (int*)malloc(sizeof(int) * 4);
    int i;
  
    // p[] contains some garbage value so
    // below loop does not make any sense.
    for (i = 0; i < 4; i++)
        p[i] += 100;
}

A solution is to use calloc() instead which initialize block to 0.

Buffer overflow : This is a very common mistake that occur in C and this become even more common due to presence of a faulty function in C itself i.e. gets() function which is used to take string as input. It does not check the memory provided to store string in the program due to which if a user enter string of greater size then gets() with overwrite memory location after the string and cause overflow.




void read()
{
    char str[20];
    gets(str);
    printf("%s", str);
    return;
}

The code suffers from Buffer Overflow as gets() doesn’t do any array bound testing. gets() keeps on reading until it sees a newline character. To avoid Buffer Overflow, fgets() should be used instead of gets() as fgets() makes sure that not more than MAX_LIMIT characters are read.




#define MAX_LIMIT 20
void read()
{
    char str[MAX_LIMIT];
    fgets(str, MAX_LIMIT, stdin);
    printf("%s", str);
    return;
}

Memory leaks This situation arises when the used heap memory is not de-allocated due to which the main memory get eventually filled up and free memory become less.




/* Function with memory leak */
#include <stdlib.h>
  
void f()
{
    int* ptr = (int*)malloc(sizeof(int));
  
    /* Do some work */
  
    return; /* Return without freeing ptr*/
}

We should use free() after malloc() if memory is not used anymore.




/* Function without memory leak */
#include <stdlib.h>
  
void f()
{
    int* ptr = (int*)malloc(sizeof(int));
  
    /* Do some work */
  
    free(ptr); // Deallocate memory
    return;
}

Bug due to precedence Less understanding of operator and their precedence can produce a bug especially with pointers like




// C program to demonstrate bug introduced due
// to precedence.
#include <stdlib.h>
  
int demo()
{
    int a = 10;
    int* p = &a;
  
    // intention was to increase the value of a
    *p++;
}

Precedence of * (dereference/indirection operator not multiplication) and postfix ++ are not same but prefix ++ and * has same, and hence, first the value of p will increase and will point to a bad memory area and then dereference and will overwrite that location or program may get terminated. Please see Difference between ++*p, *p++ and *++p for details.

Sending address of non-existing variable Returning address of a local variable causes problems,




#include <stdlib.h>
  
int fun()
{
    int x = 10;
    return &x;
}
int main()
{
    int* p = fun();
    *p = 20;
}

When function fun() is called variable a is created but as soon function returns, it get destroyed. Since function is returned its address p will point to a memory area in stack area and if another function is called then a change by pointer p may result in error.

Pointer arithmetic Pointer arithmetic can be confusing let’s take an example suppose integer is of 4 Bytes.




int main()
{
    int x[10] = { 0 }, i = 0, *p;
  
    // p point to starting address of array x
    p = &x[0];
    while (i < 10) {
        *p = 10;
  
        // intention was to point to integer at x[1]
        p = p + 4;
        i++;
    }
}

Although it seems correct as integer is of 4 byte and p is at starting location so adding 4 to it will cause p to point at next integer in array n but pointer arithmetic work according to size of its data type so adding 1 to a pointer of integer then sizeof(int) will get added to it same applies for pointer to any other data type.




int main()
{
    int x[10] = { 0 }, i = 0, *p;
    p = &x[0]; // p point to starting address of array x
    while (i < 10) {
        *p = 10;
        p++; // means p = p + sizeof(int)
        i++;
    }
}

Passing array as parameter When we pass an array to a function, it is always treated as a pointer in the function. That’s why we should never use sizeof on array parameter. We should rather always pass size as a second parameter.




#include <stdio.h>
  
// arr is a pointer even if we have
// use square brackets.
void printArray(int arr[])
{
    int i;
  
    /* sizeof should not be used here to get number 
    of elements in array*/
    int arr_size = sizeof(arr) / sizeof(arr[0]); 
    for (i = 0; i < arr_size; i++) {
        printf("%d ", arr[i]);
    }
}
  
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    printArray(arr);
    return 0;
}

Below is the corrected code




#include <stdio.h>
  
// arr is a pointer even if we have
// use square brackets.
void printArray(int arr[], int arr_size)
{
    int i;
    for (i = 0; i < arr_size; i++) {
        printf("%d ", arr[i]);
    }
}
  
int main()
{
    int arr[] = { 1, 2, 3, 4 };
    int arr_size = sizeof(arr) / sizeof(arr[0]); 
    printArray(arr, arr_size);
    return 0;
}

Reference :
Computer Systems :A programmer’s Perspective


Article Tags :