Common Problems Encountered in C

The following is a list of some of the problems that new (and old) users of C often stumble across. Symptoms of the problem and ways to avoid the problem are mentioned where possible. There is also a map of symptoms to possible causes.
= vs ==
Symptom: The body of an if, while or for statement is always (or is never) executed when it (apparently) shouldn't be.
Description: = used by mistake in a conditional context instead of ==.
Avoid by: Use gcc -Wall: for a piece of code like
		if (i = 1009)
			printf("Why is this always printed?\n");
	
gcc will say something like
	foo.c:42: warning: suggest parentheses around assignment used as truth value
	
If you really intend to use an assignment, you can re-write it as
		if ((i = 1009))
			printf("Why is this always printed?\n");
	
and gcc will cease to complain.

Missing & in call to scanf
Symptom: Segmentation fault (core dumped)
Description: When calling scanf(), the arguments corresponding to %d, %u, %o, %x, %i, %n, %e, %g, %f and %c must be pointers to integers, floats, doubles or characters. If you pass an integer instead of a pointer to an integer (e.g., x instead of &x), you will probably get a core dump.

Note that although %s and %[ take pointers, the & is usually not required as C passes arrays by reference.
Avoid by: Use gcc -Wall: it will check that the arguments to scanf, fscanf and sscanf match the format argument (it will also check the format and arguments of printf, fprintf and sprintf).

Not including math.h
Symptom: Routines like atof, sin, cos, etc, appear not to return reasonable values.
Description: If a routine which returns a float or double is not declared as such, the compiler will assume it returns an int; this usually leads to very strange values being returned.
Avoid by: Use gcc -Wall: it will complain about the use of routines that have not been declared. Unfortunately, the header files on some systems (notably, SunOS 4.x) are not complete and gcc will complain about routines like printf, exit, etc not being declared - one must learn to ignore such errors (or add the declarations explicitly in your source).

Arrays are 0 based, not 1 based
Symptom: Values of variables changing when they haven't been assigned to.
Description: The first element in an array in C is at index 0 (not index 1), and the last index is n-1 (not n), where n is the number of elements in the array. People often forget this and write loops like
		for (i = 1; i <= n; i++)
			array[i] = ...;
	    
when they should write
		for (i = 0; i < n; i++)
			array[i] = ...;
	    
Since C does no run time bounds checking on array references, references to array[n] refer to the memory just past the end of the array - there is a very good chance that some other variable happens to reside at this location, and it will be read or written by accident.
Avoid by: Sorry - no easy avoidance.

Not initializing variables
Symptom: A function behaves differently each time it is called (when it should behave the same).
Description: When a variable is declared in a function space is allocated for it on the stack - the initial value of the variable is what ever happened to be on the stack, usually a fairly random value that will change between invocations of the function. Similar things can happen when memory allocated using malloc or realloc is not initialized before being used.
Avoid by: Can be partly avoided using gcc -Wall -O: it will complain about the use of variables that have not been initialized; however it will not complain about use of array elements (or structure fields) that have not been initialized. In the case of allocated memory, use calloc instead of malloc - it will zero the newly allocated memory.

Not allocating memory for pointers
Symptom: Segmentation fault (core dumped)
Description: The declaration
		char *p;
	    
does not associate any memory with p - if you use it without giving it any memory (e.g., strcpy(p, "hi there")), the memory references will be some random location (see section on uninitialized variables).
Avoid by: Can be partly avoided using gcc -Wall -O: it will complain about the use of variables that have not been initialized; however it will not complain about use of array elements (or structure fields) that have not been initialized.

Allocating 1 too few bytes for strings
Symptom: Segmentation fault (core dumped)
Description: When allocating space for strings, don't forget that you need to allocate space for the trailing null byte. A common error is code like:
		char *p = (char *) malloc(strlen(str));
		strcpy(p, str);
	    
this does not allocate enough space for the string - it should be
		char *p = (char *) malloc(strlen(str) + 1);
		strcpy(p, str);
	    

Avoid by: Use strdup to copy strings (unfortunately, this routine is not available on all machines, so this too can lead to problems).

Use of memory after is has been freed
Symptom: Segmentation fault (core dumped);
variables change values without being called.
Description: When allocated memory is deallocated using the free, the memory may be modified immediately by the code in free (it uses memory to perform its bookkeeping functions) or the memory may be re-used by the next call to malloc. In either case, if you continue to use the memory after freeing it, the memory can be modified unexpectedly, or your modification of it may cause other parts of your program to act strangely.
Avoid by: Sorry - no easy avoidance.

Calling functions with incorrect arguments
Symptom: Segmentation fault (core dumped);

Description: If a function is not declared before it is used, the C compiler has no idea what kind of arguments the function takes, and therefore lets you pass anything to the function. This can lead to passing too many, too few or simply the wrong kind of arguments to functions.
Avoid by: Use gcc -Wall: it will complain about the use of routines that have not been declared.
Always declare functions with their arguments (i.e., don't declare a function as foo()).

Syntax error at end of header file
Symptom: C compiler complains about apparently valid code.
Description: If a syntax error (e.g., a missing semi-colon) occurs at the end of a header file, the C compiler may complain about the next line of code, which can be in a different file. This can lead to difficulties in locating the actual error (can also lead to loss of hair).
Avoid by: When dealing with compiler errors with no apparent cause, check the line of code immediately preceding the error - the problem may be above a preceding block of comments, or, if the preceding line is a #include, the problem may be in the header file.

Returning pointer to stack memory
Symptom: A buffer (or any memory) initialized by a function is modified when the function returns (or after another function is called).
Description: When variables are declared in a function, the memory for the variables is allocated on the stack; when the function returns, the memory is re-used by the next function that is called. This means that the buffer in the following piece of code can get trashed after the function returns:
	    char *
	    foo(char *str1, char *str2)
	    {
		char buf[1024];

		strcpy(buf, str1);
		strcat(buf, str2);
		return buf;
	    }
	    ...
		printf("Why isn't `%s' `hi there'?\n", foo("hi", " there"));
	    ...
	    
(the code is also bad as it assumes buf is big enough to hold str1 and str2)..
Avoid by: If memory is to be used after a function returns, it should be either be dynamically allocated using malloc() and friends, or it should declared as static - in the above example, declaring buf as static char buf[1024]; will make the code work (but it still assumes buf is big enough).

The following table maps symptoms to possible causes listed about:

Program gets segmentation fault in malloc() calloc() realloc() or free().
Program gets segmentation fault in scanf() fscanf() sscanf().
Program gets segmentation fault in printf() printf() printf().
Program gets segmentation fault.
Routines like atof, sin, cos, etc, appear not to return reasonable values.
C compiler complains about valid code
Function behaves differently between invocations
The body of an if, while or for statement is always (or is never) executed when it (apparently) shouldn't be.
Values of variables changing when they haven't been assigned to.