I've been programming in 'C' and 'C++' for a few years now, along the way I've learned a thing or two that have become part of my personal best coding practices and I thought I would pass these tidbits on for others to use. These hints, while written with the 'C' and C++ in mind, can be applied to other structured programming languages as well.
When coding, keep this firmly in mind: someone (most likely you!) will have to revise and maintain the code your writing today at some point in the future. Will you be able to understand the code you're currently writing six months from now? A year from now? If not then you need to change some things.
Do not comment the obvious
But if you have done something rare, elegant, crafty, or otherwise tricky then comment the hell out of it. "Voodoo Programming" is not a viable method for ensuring job security.
It is common practices (especially in Windows programming) these days to use what is called Hungarian Notation as a naming convention. This notation seeks to encode type information in a name as a visual clue to the programmer, in an effort to eliminate data type mismatches. The fact is that most compliers will always catch type mismatch type problems.
int iCount; // An integer variable.
int iGetPageCount(DOC *ThisDoc); // Returns an int
iCount = iGetPageCount(MyDoc); // Types match.
The problem with this practice is that it detracts from the maintainability of the code. If you need to change the iGetPageCount() function (above) to return a long value, you have to wade through your source, looking for every occurrence of the function name and change it to lGetPageCount() and you have to change the name of every variable that receives a value in a call to that function - what a maintenance nightmare!!
Without using the Hungarian Notation conventions all that needs to be changed is the type of the function and the variables that receive its values. For this reason alone I have given up using this convention.
Giving up Hungarian Notation has saved me many hours that would otherwise be worthlessly spent maintaining my naming convention.
When dealing with large, daunting projects, it is very easy to get tied up in trying to plan and design the whole thing at one time. This is usually counter productive. Break the problem down into smaller logical units. If each of these is still too big, then break them down into yet smaller logical units. Start designing when you have a piece that you can get your mind around. I've often found it beneficial to design and build a small piece, test it, then go on to the next piece, rather than designing them all at once. Do the coding while the design is fresh in your mind.
Test, test, test!
Test the small pieces of your project as individual units; make sure they work as planned. If they do work then try passing them bad data elements, just to see what happens. Yeah, yeah I know that subroutine will never be called with a value that big! Yeah right. Been there, done that, spent hours tracking down the problem. Protect your code from bad data!
Build test harnesses into your code
Include a main() function in each module (surrounded by conditional compilation macros) so you can compile and test it in isolation from other modules. If you've written a library take the time to write a module that includes a main function that does nothing except test the library routines.
Read, read, read!
Do you read Dr. Dobbs, Software Development, or the C Users Journal? If not - why not? You think you know everything? I learn something from each of these magazines every single month. "So what?" you may ask? Well, let me put it this way; I hope you are the competition for my next job.