One of the better tools that I have found for picking up a large variety of potential bugs is gcc when invoked with the right flags. The flags I prefer to use are:
-ansi -pedantic: best to compile in ANSI mode.
-W -Wall: enable a bunch of warnings.
-Wundef: undefined macro variables used in #if.
-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations: make sure that functions are prototyped properly.
-O2: enable enough optimizations that gcc can identify variables that may be used before being defined.
There are other options that you may or may not wish to use. In particular, the -Wcast-qual and -Wwrite-strings are useful if you are starting a project from scratch, and are willing to add const to your function prototypes. -Wtraditional is useful if you are going to have to port to systems for which no ANSI compiler is available.
One problem that arises is that to use non-ANSI features and still compile cleanly with gcc, we have to do a little extra work. For instance, using alloca() will cause gcc to report a bunch of warnings. So, we modify xmemory.h as follows:
#if( __GNUC__ )
#define alloca(x) __builtin_alloca(x)
#endif
This modification causes gcc to quietly compile programs that use alloca().