In the words of Doug Gwyn, “Unix was not designed to stop you from doing stupid things, because that would also stop you from doing clever things”. C is a very powerful tool, but it is to be used with care and discipline. Learning this discipline is well worth the effort, because C is one of the best programming languages ever made. A disciplined C programmer will…
Prefer maintainability. Do not be clever where cleverness is not required. Instead, seek out the simplest and most understandable solution that meets the requirements. Most concerns, including performance, are secondary to maintainability. You should have a performance budget for your code, and you should be comfortable spending it.
As you become more proficient with the language and learn about more features you can take advantage of, you should also be learning when not to use them. It’s more important that a novice could understand your code than it is to use some interesting way of solving the problem. Ideally, a novice will understand your code and learn something from it. Write code as if the person maintaining it was you, circa last year.
Avoid magic. Do not use macros1. Do not use a typedef to hide a pointer or avoid writing “struct”. Avoid writing complex abstractions. Keep your build system simple and transparent. Don’t use stupid hacky crap just because it’s a cool way of solving the problem. The underlying behavior of your code should be apparent even without context.
One of C’s greatest advantages is its transparency and simplicity. This should be embraced, not subverted. But in the fine C tradition of giving yourself enough rope to hang yourself with, you can use it for magical purposes. You must not do this. Be a muggle.
Recognize and avoid dangerous patterns. Do not use fixed size buffers2 – always calculate how much space you’ll need and allocate it. Read the man pages for functions you use and handle their failure modes. Immediately convert unsafe user input into sanitized C structures. If you later have to present this data to the user, keep it in C structures until the last possible moment. Learn of and use extra care around sensitive functions like strcat.
Writing C is sometimes like handling a gun. Guns are important tools, but accidents with them can be very bad. You treat guns with care: you don’t point them at anything you love, you exercise good trigger discipline, and you treat it like it’s always loaded. And like guns are useful for making holes in things, C is useful for writing kernels with.
Take care organizing the code. Never put code into a header. Never use the inline keyword. Put separate concerns in separate files. Use static functions liberally to organize your logic. Use a coding style that gives everything enough breathing room to be easy on the eyes. Use single letter variable names when their purpose is self-evident and descriptive names when it’s not, and avoid neither.