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
Please Login to comment...