Our DMS Software Reenginering Toolkit with its C front end is designed to do this.
DMS provides general parsing and transformation capability. The C Front End builds on top of DMS, knows many dialects of C, can parse them to ASTs including preserving in many cases preprocessor conditional.
One then uses DMS's rewrite capability to make changes to the AST. Rather than hacking at the tree nodes directly, one usually writes a rule conceptually of the form:
if (sourceCodePattern) and condition then replacementCodepattern
where sourceCodePattern and replacementCodePattern are fragments of C code with variable placeholders. An example:
rule make_autoinc(l: lefthandside, e:expression):statement->statement
" \l = \l + \e ; " -> " \l++; ";
After all your rewrite rules have been applied, DMS can then prettyprint the modified source code back to a file, preserving indentation, number radix, comments etc as perfectly valid, compilable source code.
What you do with it after that is your business.
The C front end can also build symbol tables and construct control and data flow information, which is often really necessary to carry out desired changes.