C Programming Tips and Tricks

Abstract
This document is the result of me being thrown back into C programming after a few years in absense. When I was in school I learnt basic C, C++ and programming under windows. In my current job I was required to re-write some perl code in C. Following are some things that I stumbled on and that I feel others could benifit from. Learn through my failures so to speak :)

Conversions

Be careful when doing conversions from strings to numbers, and ensure that you are using the correct function. At one point I wrote some code that looked like:

unsigned long bytes;
...
bytes = (unsigned long)atoi( s );  // save bytes for later

Even though I was casting as a long, the atoi() function converts to int, and not long. The code worked for a fair amount of time, until the value stored in s grew more than the size of an int, and then suddenly 0 was returned. The atoi() will not produce an error on fail, but simply bails and returns 0. To solve this problem use atol() instead.

Fork()ing, Daemons and Servers

At some point you will be called to write a server, daemon, or some program that simply sits and waits for input.

To make a program put itself in the background and going into "daemon mode" do the following:

void main( int argc, char** argv )
{
	pid_t pid = 0;
	/* set stuff up */
	/* accept command line args */
	
	pid = fork();
	if( pid == 0 ) {
		/* this is the "background" process.  Execute process loop here */
	}
	else {
		/* "foreground" process exits */
		exit(0);
	}
}

This makes the program duplicate itself into a "parent" and "child" process. The PID returned for the child will be 0, while the parents PID will remain the same (something != 0). If the PID is 0, continue to process, if not, exit. The program will run in the background just like a good daemon should!

Some other tips for doing this would be:

Signals

Mentioned later, sometimes you'll want a program to exit cleanly, instead of killing it with CTRL-C. This is fine for normal programs, which run from start to finish, but what about servers, which are designed to run forever? Try adding something like this:

#include <signal.h>

static void catch_sigint( int );

int main( int argc, char** argv ) {
   struct sigaction sa_old;
   struct sigaction sa_new;

   // set up signal handling
   sa_new.sa_handler = catch_sigint;
   sigemptyset(&sa_new.sa_mask);
   sa_new.sa_flags = 0;
   sigaction(SIGINT, &sa_new, &sa_old );

	/* continue */
	while(1) {
		/* process loop that goes on forever */
	}
}

static void catch_sigint( int signo ) {
   Log("Caught SIGINT, exiting...");
 	/* final cleanup stuff */
   exit (1) ;
}

In this case, the program will loop forever, until you hit CTRL-C (SIGINT) and then it will execute the catch_sigint() function and do whatever you have in it. You could also use this to make sure your program does not die on the user hitting CTRL-C.

Debugging

There are many different ways to do debugging, and many different methodologies to it. Some things I suggest (and your milage may vary) would be:

gdb

This is the Gnu DeBugger. It rocks. I don't know a whole lot about it, but here is what I do with it.

Compile your programs with -ggdb this makes a large binary, but the size is because of extra information included specifically for gdb.

Run your program with $ gdb ./progname. You'll be presented with a (gdb) prompt. Type run -args (where -args are any command line args that you'd like to pass to "progname"). Gdb also mimicks a shell in that typing "make" at the (gdb) prompt will execute make in the current directory. Result? If you have a makefile it gets run. Other result? One less xterm needed for development.

When in the shell, your program could puke on a segfault. If so, type "where" or "bt" (for backtrace) and you'll get a list of the tree of commands executed, ending with the one causing the segfault. Something like:

(gdb) r
Starting program: /home/alan/code/ulisten/./test 
Hostname Enterprise
gethostbyname: Unknown host

Program received signal SIGSEGV, Segmentation fault.
0x804863a in get_my_ip () at test.c:36
36		return( inet_ntoa( *(struct in_addr *)hp->h_addr ) );
(gdb) where
#0  0x804863a in get_my_ip () at test.c:36
#1  0x8048662 in main (argc=1, argv=0xbffff904) at test.c:40
#2  0x40042bcc in __libc_start_main () from /lib/libc.so.6
(gdb) 

This shows you that at line 36 in test.c something went wrong. This happened in get_my_ip which was called from main() on line 40, and so on.... If you're doing hardcore stuff and have the debug versions of libc you'll get information on where stuff broke in there too. You'll see that the only line numbers displayed were in the test program, as that's the only thing compiled with -ggdb.

Electric Fence

Electric fence (ftp://ftp.perens.com/pub/ElectricFence) is a malloc() debugging library written by Bruce Perens. It rocks. Basically how it works is this... When you allocate memory it allocates protected memory just after. If you have a fencepost error (running off the end of an array) your program will immediately exit with a protection error. Combining this with gdb you can track down most memory errors (YMMV of course :)

There are two options you can use with Electrict Fence, both controlled by environmental variables:

EF_PROTECT_BELOW=1
EF_PROTECT_FREE=1

If EF_PROTECT_BELOW is set to 1 then Electric Fence will allocate protected memory below your malloc()'d memory as well as above. This will help protect you from running off the other side of your arrays. EF_PROTECT_FREE, when set to 1, will set areas of memory that you free() with protected memory, thus not allowing you to use memory that has been free()d. This helped me fix a potentially nasty bug in something like the following code:

SafeStrncpy( tokenbuffer, strstr( buffer, "=" )+1, MAXBUF );
tokenbuffer[MAXBUF - 1] = '\0';
if( tokenbuffer ) {
	free( tokenbuffer );
}
ret = atoi( tokenbuffer );

Electric Fence immediately died when it hit the last line of code, as atoi() was trying to use free()d memory.

Combining Electric Fence with gdb, you can track down to exactly what line tried to access the protected memory.

Memwatch

This is another excellent utility for detecting memory leaks. Like electric fence, it replaces malloc() and free() (and calloc, and realloc, etc) with it's own functions that keep track of memory. Just compile in memwatch.c to your program (shown below) and when run it'll produce a report of leaked memory.

Note: this report will not work if the program crashes. It must exit cleanly to work (see section on signal handling for how to deal with this and servers/daemons).

You can retrieve memwatch from http://www.linkdata.se/sourcecode.html. It is very simple to use. Just compile the .c file in with your program with a couple of variables set.

Example Makefile:


CC=gcc
CFLAGS=-ggdb -Wall

test: test.o
$(CC) -o $@ $< -DMEMWATCH -DMEMWATCH_STDIO memwatch.o

test.o: test.c
$(CC) -c $*.c -DMEMWATCH -DMEMWATCH_STDIO memwatch.c

The above will compile the memwatch program with the MEMWATCH and MEMWATCH_STDIO flags set (which as far as I can tell, make it work). Full instructions are in the USING file in the distribution.

After you have run your program you'll find something like this in the file memwatch.log:


============ MEMWATCH 2.64 Copyright (C) 1992-1999 Johan Lindh =============

Started at Tue Oct 31 23:17:30 2000

Modes: __STDC__ 32-bit mwDWORD==(unsigned long)
mwROUNDALLOC==4 sizeof(mwData)==32 mwDataSize==32

NULL free: <290> test.c(1838), NULL pointer free'd

Stopped at Tue Oct 31 23:18:30 2000

unfreed: <31> test.c(1440), 8 bytes at 0x401a0ff4     {53 F5 CC 01 00 00 00 [deleted] }
unfreed: <4> test.c(1359), 132 bytes at 0x4019af78    {70 69 6E 67 73 00 00 [deleted] }

Memory usage statistics (global):
 N)umber of allocations made: 147
 L)argest memory usage      : 8332
 T)otal of all alloc() calls: 131968
 U)nfreed bytes totals      : 140

The contents are pretty self explanitory. If you free an already free'd or NULL pointer, it points it out. Ditto for unfreed memory. The section at the bottom shows you stats with how much memory was leaked, how much was used, total allocated, and so on.

Links

Books