Re: re: valgrind (again)
Lint (or similar static source-code analysis tool) used to be popular. Not sure what today's equivalent might be (to some extent, compilers have got better). Suggestions welcome.
There are various static analyzers for C. Later generations of lint are still available on various platforms, such as FreeBSD; of course, many of the checks it performs are now handled by some C compilers.
There are some commercial static analyzers, which justify their cost by having more extensive and ambitious checks and expressive rule languages that can be used to develop custom checks. Freeware options include splint (powerful, complicated, not entirely C11-compilant) and cppcheck (fast, less powerful, designed to reduce false positives). I occasionally run splint on some of my C code base and use cppcheck fairly often. cppcheck misses a lot but because it produces few false positives it's a useful quick check.
Just last month there was a story here about Facebook's Infer project, which employs some new techniques to create a very fast static analyzer.
Similarly, there are a number of dynamic analyzers for native code. Valgrind is certainly a useful tool, but it's limited to Linux and like any other analyzer has strengths and weaknesses. A number of UNIX platforms come with heap checkers and similar tools; for Windows, there are the debug versions of Microsoft's C runtime libraries. Then there are commercial products such as DevPartner (the former NuMega / BoundsChecker product, now owned by Micro Focus, my employer).
With dynamic testing, it's also useful to employ a smart whitebox fuzzing engine like Microsoft's SAGE, which can analyze flow paths in running programs and (using a constraint solver) figure out what inputs will drive different flows.
There's a ton of research in this area. One of the great tragedies of software development is that so little of it is applied to most software. Really anyone writing C code that's going to be used for any nontrivial purpose should be running at least one static analyzer against it and testing under at least one dynamic-analysis framework; that should be the very minimum programmers consider acceptable.