This posting has a list of UNIX books which includes most of the classic C/Unix works. For C programming on Windows, Petzold's Programming Windows is probably the best start.
For C program design, some of the UNIX programming books will tell you snippets but I'm not aware of a 'C program architecture' book.
Make use of stack. Often when you call a procedure you will want to have variables allocated in the caller's stack frame and pass pointers to them into the procedure you want to call. This will be substantially faster than dynamically allocating memory with malloc() and much less error-prone. Do this wherever appropriate.
C doesn't do garbage collection, so dynamically allocating data items is more fiddly and you have to keep track of them to make sure they get freed. Variables allocated on the stack (see 1) are more 'idiomatic' where they are applicable. Plus, you don't have to free them - this is a bonus for local variables.
Apropos of (2), consider an architecture where your functions return a status or error code and pass data in and out using the stack as per (1).
Get to know what setjmp() and longjmp() do. They can be quite useful for generic error handler mechanisms in lieu of structured exception handling functionality.
C does not support exceptions. See (3).
Lint is your friend. Splint is even friendlier.
Learn what the preprocessor does and what you shouldn't do with it even if you can.
Learn the ins and outs of endian-ness, word alignment, pointer arithmetic and other low-level architectural arcana. Contrary to popular opinion these are not rocket science. If you're feeling keen, try dabbling in assembly language and get a working knowledge of that. It will do much for your understanding of what's going on in your C program.
C has no concept of module scope, so plan your use of includes, prototype declarations, and use of extern and static to make private scopes and import identifiers.
GUI programming in C is tedious on all platforms.
Apropos of (10) learn the C API of at least one scripting language such as Tcl, Lua or Python. In many cases, the best use of C is as a core high-performance engine on an application that is substantially written in something else.
The equivalent of a constructor is an initializing function where you pass in a pointer to the item you want set up. Often you can see this in the form of a call to the function that looks like setup_foo(&my_foo)
. It's better to separate allocation from initialising, as you can use this function to initialise an item you have allocated on the stack. A similar principle applies to destructors.
Most people find Hungarian notation about as readable as written Hungarian. The exception to this is native Hungarian speakers, who typically find Hungarian notation about as legible as Cuneiform.. Unfortunately, Hungarian notation is widely encountered in Windows software and the entire Win32 API uses it, with the expected effects on the legibility of software written on this platform.
C/Unix books, even really good ones like the ones written by the late W Richard Stevens tend to be available secondhand quite cheaply through Amazon marketplace. In no particular order, get a copy of K&R, Stevens APUE and UNP 1 & 2, the Dragon book, Rochkind, Programming Pearls, Petzold and Richter (if working on Windows) and any of the other classic C/Unix works. Read, scribble on them with a pencil and generally interact with the books.
There are many, many good C/Unix programming resources on the web.
Read and understand the Ten Commandments of C Programming and some of the meta-discussion as to the why's and wherefores behind the commandments. This is showing its age to a certain extent, although most of it is still relevant and obscure compilers are still quite common in the embedded systems world.
Lex and Yacc are your friend if you want to write parsers.
As Navicore points out below (+1), Hanson's 'C Interfaces and Implementations' is a run-down on interface/implementation design for modular architecture with a bunch of examples. I have actually heard of this book and heard good things about it, although I can't claim to have read it. Aside from the C idioms that I've described above, this concept is arguably the core of good procedural design. In fact, other procedural languages such as Modula-2 actually make this concept explicit in their design. This might be the closest thing to a 'C Program Architecture' book in print.
Read the C FAQ.