This post lists some common memory errors in the C programming and provides a demo to show how to detect the memory errors by the Valgrind.

The pointers and memory management brother every programmer with C. It truly leads to some strange errors and costs a lot of debugging time for developers. In my opinion, a good and standard programming custom really helps to prevent memory errors. The memory errors could be very subtle. Fortunately, The Valgrind tools can automatically detect many memory management and threading errors. It really helps me to improve my scientific codes in the Madagascar.

The memory errors can be crossly classified into Heap memory errors and Stack memory errors. Some of the common and subtle errors are:

  • invalid memory access
  • memory leak
  • cross stack access

Typically, the compiler won’t give any warnings or error messages and the program can be executed normally without segmentation fault. Unfortunately these errors might lead to unexpected results when dealing with large scale calculation. I provides a simple demo with an allocation operation of structure in C. It contains enough comments and is written in standard custom (in my opinion :P). It might inspire you to debug your own code.

ValgrindDemo:

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

/* Defining structs */
typedef struct DemoStruct *demostruct;
/*^*/
struct DemoStruct{
	float **array;                              /* a two-dimension array */
	int n1;
	int n2;
};
demostruct init_struct(int n1, int n2);
void free_struct(demostruct demo);

int main(int argc, char* argv[]) 
{
	int n1=2;
	int n2=2;
	int ix, iz;

	/* problem 1 (cross stack access): If you let pointer A equals to pointer B, the original memory belongs to A will leak. */
	demostruct StructA = (demostruct) malloc(sizeof(*StructA));
	if(StructA == NULL) {
		fprintf(stderr,"Error in allocation memory!\n");
	}
	StructA = init_struct(n1, n2); 	
	/* solution of 1: directly initial struct */
	//demostruct StructA = init_struct(n1, n2); /* alloc memory for struct */

	for (ix=0; ix<StructA->n2; ix++)
		for (iz=0; iz<StructA->n1; iz++)
		{
			StructA->array[ix][iz] = ix + iz;
		}

	/* problem 2 (invalid access): Memory overwrite, invalid write of size 4 */
	StructA->array[1][2] = 4;
	/* solution 2 Check Memory */
	//StructA->array[1][2] = 4;

	fprintf(stderr, " Array size: n1=%d, n2=%d\n",StructA->n1, StructA->n2);
	fprintf(stderr, " Array element:\n %3f\t %3f\n %3f\t %3f\n",StructA->array[0][0], \
			StructA->array[0][1],StructA->array[1][0], StructA->array[1][1]);
	free_struct(StructA);
	StructA=NULL;
	exit(0);
}

demostruct init_struct(int n1, int n2)
{
	demostruct demo = (demostruct) malloc(sizeof(*demo));
	if(demo == NULL) {	/* It is essential to check if there is enough memory to alloc your pointers */
		fprintf(stderr,"Error in allocation memory!\n");
	}
	demo->n1 = n1;
	demo->n2 = n2;
	demo->array = sf_floatalloc2(n1, n2);         /* every pointer needs to be initialized in C */
	memset(demo->array[0], 0, demo->n1*demo->n2); /* The fastest way to initial an array to avoid garbage data */
	return demo;
}

void free_struct(demostruct demo)
{
	/* problem 3 (memory leak)*/

	/* solution 3 every pointer needs to be freed, and it should prior to free(demo)*/
	//free(demo->array[0]);                       
	//free(demo->array);
	
	free(demo);
	demo=NULL;                                  /* let the demo point to NULL to avoid wild pointer */
}

compiling and executing script:

gcc -g ValgrindDemo.c -o ValgrindDemo.x -I$RSFSRC/include -L$RSFSRC/lib -lrsf  
valgrind --leak-check=yes ./ValgrindDemo.x

Results:

==2753== Memcheck, a memory error detector
==2753== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.
==2753== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==2753== Command: ./ValgrindDemo.x
==2753==
==2753== Invalid write of size 4
==2753== at 0x401B1E: main (ValgrindDemo.c:42)
==2753== Address 0x51fc140 is 0 bytes after a block of size 16 alloc’d
==2753== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2753== by 0x401CE5: sf_alloc (alloc.c:49)
==2753== by 0x401E51: sf_floatalloc (alloc.c:121)
==2753== by 0x40225E: sf_floatalloc2 (alloc.c:254)
==2753== by 0x401C40: init_struct (ValgrindDemo.c:61)
==2753== by 0x401A9B: main (ValgrindDemo.c:30)
==2753==
Array size: n1=2, n2=2
Array element:
0.000000 1.000000
1.000000 2.000000
==2753==
==2753== HEAP SUMMARY: ==2753== in use at exit: 48 bytes in 3 blocks
==2753== total heap usage: 4 allocs, 1 frees, 64 bytes allocated
==2753==
==2753== 16 bytes in 1 blocks are definitely lost in loss record 2 of 3
==2753== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2753== by 0x401A63: main (ValgrindDemo.c:26)
==2753==
==2753== 32 (16 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==2753== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2753== by 0x401CE5: sf_alloc (alloc.c:49)
==2753== by 0x402249: sf_floatalloc2 (alloc.c:253)
==2753== by 0x401C40: init_struct (ValgrindDemo.c:61)
==2753== by 0x401A9B: main (ValgrindDemo.c:30)
==2753==
==2753== LEAK SUMMARY:
==2753== definitely lost: 32 bytes in 2 blocks
==2753== indirectly lost: 16 bytes in 1 blocks
==2753== possibly lost: 0 bytes in 0 blocks
==2753== still reachable: 0 bytes in 0 blocks
==2753== suppressed: 0 bytes in 0 blocks
==2753==
==2753== For counts of detected and suppressed errors, rerun with: -v
==2753== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)