display | more...

(Completely uprooted and shoddily recommented due to Blatant Cut-and-paste Infringement from Wiretap's Treasure Chest =)

The Ten Commandments for C Programmers was written by Henry Spencer, an Usenetter and C and Unix guru (he wrote a lot of cool stuff like BSD regex libraries, getopt, and stuff like that). He also worked on ANSI C and POSIX standards.

The following is simplification and summarization of Spencer's own commentary, and some modern hindsight on the matters. (I'm not exactly a C guru, but I try...)

I. Thou shalt run lint frequently and study its pronouncements with care, for verily its perception and judgement oft exceed thine.

What does this mean? Well, back in the day, and even these days, there's a program called lint that examines the souce and will give warnings on possible mistakes on the souce code. These days, most of the advice is often rolled in the compiler (GCC gets pretty verbose when you invoke it with -Wall), even though there still are separate tools to do this checking. Trust the compiler warnings, use the verbose compiler error finding features as commonly as possible. Ignore this only if you are 100% sure it's wrong or irrelevant. If you use compiler warnings or lint-like tools, you may find that your program is pretty neat in comparison to something that has been thrown together without any compiler warnings at all. Thus, Enable The Warnings.

II. Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

What does this mean? The C programming language has this odd thing called pointers - something which is very handy for handling things by reference. Pointers can point to any memory location - or none at all. It is programmer's job to make sure that their pointers point at the correct things, or at least somewhere. You can't follow a pointer that doesn't point anywhere (a null pointer), and in modern OSes you can't follow a pointer that points to area not allocated by the program. Thus, Check The Pointers.

III. Thou shalt cast all function arguments to the expected type if they are not of that type already, even when thou art convinced that this is unnecessary, lest they take cruel vengeance upon thee when thou least expect it.

What does this mean? Spencer seems to be outdated here. Typecasting has changed a lot in the ANSI standards in regards to K&R practices. Previously, the advice frequently was "cast the hell out of things", but these days, it's more like "DON'T, and that goes darn well to the damn pointer types". I suppose non-pointer types still benefit from casting, but I'm extremely worried of the pointer casting.

I believe you don't need to typecast the NULL or 0 when you set pointer to la-la land, though - a NULL or 0 is understood to be a pointer to nowhere, no matter what type it is. ariels says you need to cast NULL when passing it as an argument to a varargs function, because otherwise it's interpreted as int. Most notably, malloc() should not be typecast these days (see Casting the return value of malloc()).

IV. If thy header files fail to declare the return types of thy library functions, thou shalt declare them thyself with the most meticulous care, lest grievous harm befall thy program.

What does this mean? Get out of the stone age. =) Not sure how relevant this is, I'm pretty sure all C library suppliers are fairly modern. Fairly. If not, we feel your pain.

V. Thou shalt check the array bounds of all strings (indeed, all arrays), for surely where thou typest "foo" someone someday shall type "supercalifragilisticexpialidocious".

What does this mean? C doesn't do bounds checking on arrays. This is one of the big weaknesses of the language according to its critics. When operating on strings or arrays, you have to make sure you are not accessing memory outside the allotted space.

On the age of modern OSes, it's difficult to clobber anything important belonging to another process, but if you manage to leave a hole to your bounds checking, it's possible to clobber the program's own variables and run-time data - and thus the stack. In other words, Anyone can tell your the program what location of the memory to execute next, which just might be some of the garbage that the attacker fed to your unchecked buffer. We don't want that to happen, do we?

At least, if you don't properly handle the bounds of your array, stuff will crash or behave weirdly if someone puts too much stuff in them.

Spencer also points at the evils of gets(), which has been deprecated to Hell in all proper C libraries and your compiler will complain LOUDLY if you try to use it. That is, in the "proper" systems. Improper systems should be... fixed.

Spencer mentions The Great Worm as an example of buffer overrun attack. These days, the buffer overflows are utilized by numerous worms (Code Red still pounding on my chamber door...)

VI. If a function be advertised to return an error code in the event of difficulties, thou shalt check for that code, yea, even though the checks triple the size of thy code and produce aches in thy typing fingers, for if thou thinkest "it cannot happen to me", the gods shall surely punish thee for thy arrogance.

What does this mean? Implement error checking in your subroutines, and process possible error conditions that the library stuff has. Yes, this leads to long and boding coding sessions, but it's better to have an informatively crashing program with voluminous error messages, than have a program that can only print "Segmentation fault" - or proceed to do something stupid at step 568 when step 2 failed. Some other languages have Exceptions. C coders will need to remember perror(). Get used to it.

VII. Thou shalt study thy libraries and strive not to reinvent them without cause, that thy code may be short and readable and thy days pleasant and productive.

What does this mean? "If it ain't broke, don't fix it." Yes, sometimes, libc sucks. No, it's called a "shared library" for a reason.

Don't reinvent the wheel if you can help it - especially if you aren't sure if you know a "more efficient" way. If you reimplement it out of ignorance of the capabilities of the library, just make your stuff use the library code.

VIII. Thou shalt make thy program's purpose and structure clear to thy fellow man by using the One True Brace Style, even if thou likest it not, for thy creativity is better used in solving problems than in creating beautiful new impediments to understanding.

What does this mean? 1TBS may or may not be the greatest brace style. Heck, it doesn't actually matter what brace style you use, as long as your project uses one coding style consistently! I'm not going into the war of seeing which brace style rules and which doesn't - all probably rule one way or other.

And if you're doing work in a group, stick to the style used by everyone else. If all developers use different styles, get the hell out of the project as fast as you can.

IX. Thy external identifiers shall be unique in the first six characters, though this harsh discipline be irksome and the years of its necessity stretch before thee seemingly without end, lest thou tear thy hair out and go mad on that fateful day when thou desirest to make thy program run on an old system.

What does this mean? Your globals should be "Unique to the first six letters", not "limited to six letters". This is in ANSI, I hear, because of utterly painful "obsolescence" of some linkers. Hopefully ANSI will some day say "linkers will have to do longer names, or they'll cry and do longer names". (And all rational people will just ignore this commandment and tell people to upgrade their 2-penny linker - this may not get you friends, or make warnings happy...)

X. Thou shalt foreswear, renounce, and abjure the vile heresy which claimeth that "All the world's a VAX", and have no commerce with the benighted heathens who cling to this barbarous belief, that the days of thy program may be long even though the days of thy current machine be short.

What does this mean? C was made a portable language. Optimally, you should write more or less portable code even when you code on one platform only. This is because one platform will never truly be "one" platform; there'll be updates that change things, and new generations of the operating system - and subtle changes in the hardware itself. How "PC-compatible" is your PC, anyway?

If you develop on Linux 2.4 on IA32, some day, your stuff may be still running on Linux 18.2.6 on the as-of-yet unnamed 4096-bit Intel series, with a recompile and some minor tweaking. Portability is good.

Henry Spencer. The Ten Commandments for C Programmers (Annotated Edition). 25 November 1992.