Good Habits

Good Habits.

I include this here because over the years I have found certain traits and behaviors promote better and clearer programming practices. Some of these, and they will be obvious, you would be foolish to ignore. Some are more personal style things, and can be changed to what ever you are comfortable with.
Personal style cannot be under-estimated though. It can tell a lot about you and your way of thinking. I can now look at Id code and determine who wrote it just from personal style. And others will be able to for you too.

Parentheses placement.
This is a personal style thing. Most programmers use the

if (blah)
{
whatever
}

style of parantheses placement. However, purist C programmers adopt this style

if (blah) {
whatever
}

or even

if (blah) {
whatever }

personally, I go with the first one since it makes it all much clearer. It does tend to bloat the code a bit on screen, but better bloating than confusion I always say. I will always add the curly brackets too, even if they are not required. This makes adding extra code into the response to the if statement really easy. If the curly brackets aren’t there, then you will have to add them, and it all gets laborious. If you just get into the habit of adding them anyway, then it’s faster in the long run.

Indentation
This one is more of a ‘must do’. You’ll notice that whenever you see my code, every line is always indented depending on how deep inside of C command structure it is in. Obviously there is a practical limit to how far you can indent something, but if you get to the point were you can’t see it on you screen then you are doing something wrong, and more than likely the code needs to be broken down into more smaller processes.

New lines for multiple part if statements
When I get complicated if statements requiring many variables to be and-ed or or-ed together I tend to stick them on multiple lines, just so as I can more clearly see what the hell is going on. For instance

If (((blah = 2) ||
(x != 3) ||
(z < d)) &&
(f != g))
{
blah;
}

is much clearer to read than

if (((blah = 2) || (x != 3) || (z < d)) && (f != g))
{
blah;
}

well, I think so anywayJ.

Comments
This is a must do. You CANNOT COMMENT YOUR CODE TOO MUCH. I’m going to say that again just for emphasis.
You CANNOT COMMENT YOUR CODE TOO MUCH.
There. I hope you get this emblazoned on the inside of your skull in letters of fire. Comments are your friend. They will help you when you come back to your code six months down the line and try and figure out what the clever code actually does and how it does it. They will help the next guy who comes along to modify your code to do something else. They will help you earn more money and will get you great sex every time with who ever you want. Comment early and comment often.
Seriously, it is a very good practice to get into. Many programmer say “I don’t have the time” or “it doesn’t work yet, I’ll do it when it’s done”. This is not good enough, and any lead programmer worth his salt will be having some pretty prickly words with you of you start writing un-documented code. So get used to the idea. It will save you a lot of grief in the long term.
Remember to document not only the code itself, but also the procedures themselves. A brief description of what the process does and how it attempts to deal with it usually is enough. Any flaws, assumptions or un-catered for behavior should be documented here too.
Remember the two formats, a ‘//’ will comment just that one line, where as a ‘/*’ to a */’ is considered all comment between, new line or not.

Put version information in at the top of each c file.
When you create a new C file, try sticking a big comment block at the top with some relevant information information in it. Like this example.
/*
File image.c
Purpose : Handle all image loading and saving, plus registration in the database
Created : June 16th 1998
Author:  Jake Simpson
Version : 1.00 – JS – Created
Version : 1.01 – JS – modified file system so we can do multiple loads
Version : 1.02 – JS – Created proper windows error messaging
Version : 1.03 – JS – Added logging to loading functions
*/

You can see here how at a glance I can see who and what has modified this file, what they have done, and in what order. This can be very handy if someone else needs to maintain your code after you.

Always checking mallocs and pointers before first usage.
Once you perform a malloc, no matter how small it is, ALWAYS CHECK THE RETURN RESULT. God knows how many times I’ve found crashes in my code due to asking for some in-ordinate amount of memory due to a calculation bug, and then not checking the return result. This is something you should do everytime, since if you don’t the code will crash.
Also, everytime you come into some new process and get passed a pointer, it’s good practice to check it’s validity before using it. You may always check a malloc for a good pointer, but other people may not, and may pass you a bum pointer. I would hazard a guess that probably 85% of all the crash bugs out there in all the games available are a result of attempting to use a pointer that is pointing to either freed or just plain bogus memory.

Code grouping
Try and group your code together in a way that makes sense. Put all the file stuff together in a file named file.c, and so on. Often programmers start out with good intentions, but then soon get lazy and almost all the code ends up in one file named misc.c, or something like that. Have some discipline and in the long run you will find this helpful, since you can then transplant code from one program to another with ease.

Procedure length
I use a rule of thumb that if the procedure is more than about 150 lines long then it’s time to break it into smaller functions. I do break this occasionally, but it’s only very occasionally, and usually when I’m developing in a hurry. The 150 lines is code lines, not lines with comments in them.

Global Variables
Don’t be afraid of using global variables when you need to. Some C purists get very upset at the use of global variables, and if they are used in excess they are right. However, there are times when they are simply too damn useful to pass up. They were included in the C definition for a reason, so don’t be afraid of using them. Just don’t become dependant on them for everything either. Don’t go using them for everything just because you don’t want to have to pass anything from one process to another.

Always pass pointers, not whole data structures.
When passing info into processes, it’s almost always better to pass a pointer to a data structure than the whole structure itself. If you call a process in a repetitious manner, like a sort algorithm, then passing the whole structure every time can get mighty time consuming for the C runtime system. It copies the entire structure to the stack, goes to the process, then copies it back out again. Very wasteful when all you really need is just the memory pointer. Just remember that you are looking at the original of this structure, and be careful not to mess with it unless you are specifically meaning to. A number of times I have passed in to a procedure a pointer to a file name, then done some work on it, removing the extension, or adding a path into it to go get a file, then been surprised when I get back to the calling function that the filename has been corrupted. I needed to copy the string I was pointing at into a temporary string before doing any work to it. Don’t fall into that same trap yourself.

Naming conventions.
By and large I tend to stick with this kind of naming convention.
Typedef’d structures all have a ‘_t’ on the end of the name, and then variables that are created using this data structure use the raw name without the ‘_t’ on the end of it.
#DEFINE variables are always uppercase.
Variable names are all lowercase, with spaces between individual words. For instance
loop_counters, are_we_there_yet. And are always meaningful.
Loop counters for for-next loops and so on are almost always single letters like i, x, z, y and so on.
Procedure names are lowercase, and are either the same format as variables, or as others would have me do, are formatted so the first word is always lowercase, but all new words have the first letter capitalized. E.g.
My way
modify_names_with_path(void)
other way
modifyNamesWithPath(void)

Why the first word doesn’t get a capital I don’t know, but apparently that’s a standard. God know where it came from.

The major thing to get out of naming conventions is to make sure you decide on one and STICK TO IT. Be consistent, even if it is outlandish. Once others get into your code, they will pick this up very quickly, and they will expect you to be consistent too. I got hammered when I first started to work for Raven for using lowercase #defines, and rightly so.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>