1. Translations
2. Introduction
You may wonder what is meant by the title. Code is code, right? It's important to be bug-free and that's that, what else? Development is more than writing code and testing/debugging it. Imagine you have to read someone else's work, and I suppose you already done that, and all the variables are named foo, bar, baz, var, etc. And the code isn't commented nor documented. You will probably feel the sudden urge to invoke unknown gods, then go to the local pub and drown your sorrows. They say that you should not do unto others what you don't want done unto you, so this part will focus of general coding guidelines, plus GNU-specific ideas that will help you have your code accepted. You are supposed to have read and understood the previous parts of this series, as well as solve all the exercises and, preferably, read and wrote as much code as possible.
3. Recommendations
Before starting, please take note of the actual meaning of the word above. I don't, in any way, want to tell you how to write your code, nor am I inventing these recommendations. These are the result of years of work by experienced programmers, and many will not just apply to C, but to other languages, interpreted or compiled.
I guess the first rule I want to stress out is: comment your code, then check if you commented enough, then comment some more. This is not beneficial for others that will read/use your code, but also for you. Be convinced that you will not remember what exactly you meant to write after two or three months, nor will you know what
int ghrqa34;
was supposed to mean, if anything. Good developers comment (almost) every line of their code as thoroughly as possible, and the payoff is more than you might realize at first, despite the increased time it takes to write the program. Another advantage is that by commenting, because this is how our brain works, whatever we wished to do will be better remembered, so again you won't look at your code, fast-forward a few months, wondering who wrote your code. Or why.
The C parser doesn't really care how ordered your code is. That means you can write a typical "Hello, world" program like this, and it would still compile:
#include <stdio.h> int main(){printf("Hello, world!"); return 0;}
It seems much more readable the way we wrote it the first time, doesn't it? The general rules regarding formatting are: one instruction per line , choose your tab width and be consistent with it, but make sure that it complies with the project's guidelines, if you're working on one, also make liberal use of blank lines, for delimiting various parts of the program, together with comments, and finally, although this is not necessarily coding style-related, before you start coding seriously, find an editor you like and learn to use it well. We will soon publish an article on editors, but until then Google will help you with some alternatives. If you hear people on forums, mailing lists, etc. saying "editor x sucks, editor y FTW!", ignore them. This is a very subjective matter and what's good for me might not be so good for you, so at least try some of the editors available for Linux for a few days each before even starting to try creating some opinion.
Be consistent in variable naming. Also make sure the names fit with the others, so there is harmony within the entire program. This applies even if you're the only author of the software, it will be easier to maintain later. Create a list of used prefixes and suffixes (e.g. max, min, get, set, is, cnt) and go with them, unless asked otherwise. Consistency is the key word here.
3.1. GNU-specific guidelines
What follows is a summary of the GNU coding standards , because we know you don't like to read such things. So if you're writing code that would like to fit into the GNU ecosystem, this is the document to read. Even if you don't, it's still a good read on how to write proper code.
This document is always worth a read in it's entirety if you are creating or maintaining GNU software, but you will find the most important parts below. One first issue worth mentioning is how to deal with function prototypes. Please go back to the part dealing with that if you have any issues. The idea is "if you have your own functions, use a prototype declaration before main(), then define the function when needed." Here's an example:
#include <stdio.h> int func (int, int) int main() [...] int func (int x, int z) [...]
Use proper and constant indentation. This cannot be emphasized enough. Experienced programmers with years and years of code behind will take it very badly when you submit code with improper indentation. In our case, the best way to get used to how GNU does this is by using GNU Emacs (although this is not in any form our way to tell you that "GNU Emacs is good for you, use it.", as we're proponents of free will and choice), where the default behaviour for C code is indentation set at two spaces and braces on a line for themselves. Which brings us to another important issue. Some people use braces like this:
while (var == 1) {
code...
}
...while others, including GNU people, do it like this:
while (var == 1)
{
code...
}
Of course, this also applies to conditional expressions, functions and every occasion where you need to use braces in C code. As far as noticed, this choice is something very GNU-specific, and how much of this you respect depends solely on your taste and stance on the issue.
Our next issue is a technical one, and a promise I had to keep: the malloc() issue. Besides writing pertinent and meaningful error messages, unlike the ones we've all seen in other operating systems, check that malloc() and friends always return zero. These are very serious issues, and you'll get a few words lesson about malloc() and when to use it. By now you know what allocating memory automatically or statically is. But these methods don't cover all bases. When you need to allocate memory and have more control over the operation, there's malloc() and friends, for dynamic allocation. Its' purpose is to allocate available memory from the heap, then the program uses the memory via a pointer that malloc() returns, then said memory must be free()d. And "must" is to be written with capitals in 2 feet letters with a burning red color. That's about it with malloc(), and the reasons have already been exposed earlier in the previous part.
You are urged to use a consistent interface in all your command-line programs. If you're already a seasoned GNU/Linux user you have noticed that almost all programs have --version and --help, plus, for example, -v for verbose, if such is the case. We'll not get into all of it here; grab a copy of the GNU Coding Standards, you will need it anyway.
Although I personally tend to overlook this, and to many it's a minor issue, it will improve the readability of your code, because, again, that's how our brain works. The idea is: when you're in doubt about using spaces, use them. For example:
int func (var1, var2); int func(var1,var2);
There are some that say you can't avoid nested ifs. There are others that say "why avoid nested ifs?" And there are yet others that simply do not use nested ifs. You will create your own opinion on this as time passes and lines of code you write increase. The idea is, if you use them, make them as readable as humanly possible, as they easily can lead to almost-spaghetti code, hard to read and to maintain. And again, use comments.
The GNU coding standard say that it's good to have your code be as portable as can be, "but not paramount". Portable hardware-wise? That depends on the program's purpose and what machines you have at your disposal. We are referring more to the software side, namely portability between Unix systems, open source or not. Avoid ifdefs if you can, avoid assumptions regarding file locations (e.g. Solaris installs third-party software under /opt, while BSD and GNU/Linux do not), and generally aim for clean code. Speaking of assumptions, do not even assume that a byte is eight bits or that a CPU's address space must be an even number.
Documenting your code, in form of manual pages and well-written READMEs and so on, is another paramount aspect of software development. Yes, it IS a tedious task, but if you don't have a documentation writer on your team, it's your responsibility to do it, as every good programmer does his/her job from A to Z.
4. Conclusion
Next time we'll continue from where we left off here: going from idea to a complete program, with Makefiles, documentation, release cycles and all the fun stuff.