Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
The Ten Commandments for C Programmers (Annotated Edition) (liu.se)
41 points by b-man on Nov 6, 2010 | hide | past | favorite | 31 comments


I wonder when this was written. The earliest reference I found was June 1993 in an AUUG (Australian Unix Users Group) newsletter. http://books.google.com/books?id=lbsmK77EVU4C&lpg=PA1...

1) run lint: Not anymore. But then I'm a -Wall -Werr kind of guy and I visit Clang.

2) don't follow NULL: Well, obviously.

3) cast function arguments to the expected type: Not important now that the types are in the declarations. But if you don't force that, then yes. Got that right. Just as people got complacent with 32bit data and address, BANG!

4) declare return types for functions: That insanity has passed from practice.

5) check bounds: Yep, still causing exploits today.

6) check error codes: Yep, and triples is about right for library/system call intensive code. But the first time fclose() returns an error and you catch it you will be happy.

7) don't re-implement the library: Beating a modern C library is hard. But: if you have a special case, you can still get a significant gain by exploiting that, after you have profiled and know you aren't engaging in petty puffery. (It should be mentioned that the author is the Henry Spencer that wrote and distributed a regex library back in the days of ftp.)

8) use K&R brace styles: I care so little for brace styles I can't tell you which is K&R after writing hundreds of thousands of lines of C code.

9) external symbols must be unique in the first 6 characters: Ha ha! Good riddance SCO, the last system I met where that was an issue (in ar libraries only I think.)

10) all the world's a vax: s/VAX/x86/ e.g. I keep a PPC (byte order) and an ARM (char is unsigned) just to keep me honest on this.


I agree with you on 8) I don't see why it matters so much what style of braces are used as long as the block of code and I currently looking as it consistent. If future developers object that badly, there is also many many auto formatters out their to fix it.

the article is clearly rather dated, and a number of the points (as jws notes) are rather irrelevant now.


Two additions:

1) -Wall -Werr does not note type conversions (indeed, it could not, because they are necessary - just dangerous. -1 < 0U is usually not true!)

3) Casting is very much necessary with function taking a variable number of arguments.

But yes, modern tools and C90/C99 help a lot.


gcc -Wall does warn about comparison between signed and unsigned types, as well as comparisons that are always true/false due to limited type range.


But you can't really enable those warnings if you want to use -Werr, right?

And I've always found the "always true/false due to limited range of data type" to be counterproductive. Something like

    int i;
    ...
    assert(i <= SSIZE_MAX);
    f(i);
can make sense, but will cause gcc to complain if int and ssize_t happen to be the same size.


There are ways of fixing the warnings so you can use -Werr. For signed/unsigned, just change the type of one side of the comparison, or cast to whichever type is most correct for the comparison while manually making sure that you won't have any range issues. For your limited range example, you could probably use the preprocessor:

      ssize_t i;
  #if SSIZE_MAX != INT_MAX
      assert(i <= INT_MAX);
  #endif /* SSIZE_MAX != INT_MAX */
      f(i);


Of course, you should use ssize_t i and i <= INT_MAX...


9 - yeah though thy compiler hath longer variables beware the fashionistas who call their variables. com.sun.java.variable.main.graphics.coordinates.point.x and com.sun.java.variable.main.graphics.coordinates.point.y


Little known fact...the only reason the brace is on the same line as the function declaration is because K&R just wanted to save space in the code listings.


Um, no, K&R braces after function declaration are in the next line, because they are special.

Braces after if, for, while, switch, etc. are on the same line, and yes, it was to save space on the 80x24 screen.

Get a copy of "The C Programming Language, Second Edition". It's right in there. It's the only technical computer book I know that is still accurate after more than twenty years.

I also recommend reading the Linux kernel style guide http://www.kernel.org/doc/Documentation/CodingStyle . It's fun to read and has some good points. Sticks quite close to K&R.


I agree it's fun to read and has some good points, but it's obvious it's meant only for really imperative-styled C that you'd expect to find in the kernel. e.g.:

> ... if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program.

  class A:
    def foo(self):
      while cond:
        if othercond:
          bar()
Uh oh, four indentation levels!


Well, yes. But the style guide was written for plain C, in which the program logic starts at the first indentation in functions.

In Python you have very different characteristics, and have to adapt styles.

You should not get deeper than 3 logic indentations on function level there either, though.

And from my experience, 8 character tabs (with the article you cited actually starts with) are still workable in Python, at least for me.


Opening braces on the same line means that you can always open a new line for editing with O and o in vi and it will be a valid line for placing code on.

Very handy.


> Thou shalt not follow the NULL pointer

How much pain and misery could be avoided if hardware manufacturers would simply design computers so that memory location 0 was hard-wired to always contain a 0.

Sigh.


Wouldn't that be worse ? Instead of a coredump you'll get subtle and hard to track errors.


Presuming the CPU takes it into account in addressing modes (so that e.g. [EAX + EDX * 4 + 13], like you might see when accessing a field of an element in an array, still returns 0 if EAX is 0), you'd stil have problems similar to non-signalling NaNs vs signalling NaNs: you want the problems associated with a bug to be localized to where the bug is, and not leak out. When the bad value escapes into other code and causes problems down the line, it can be hard to figure out where the value came from.


That doesn't work if you expect struct or array at 0.

IMHO a better solution is to have a type system that has non-NULLable types, and/or use language that inserts NULL checks automatically (and optimizes them away where they're redundant).


> That doesn't work if you expect struct or array

Ah, good point.


What exactly do you think that would solve?


It handily converts your buggy pointer traversal code from SEGFAULT to infinite loop.


Why is that good? It also doesn't solve:

    int *p = malloc(LOTS);
    int v = read_from_network();
    int o = read_from_network();
    p[o] = v;
if that malloc fails, for example.


Ever heard of sarcasm?


I was sarcastic once.


Even if this were desirable, it would make much more sense for the OS (or C library) to do it rather than have it enforced by the hardware.

(edit) or, you could do it yourself. The following works for me on Linux: http://pastie.org/1278260. You may have to alter /proc/sys/vm/mmap_min_addr and/or your SELinux policy.



Any chance someone "translate" it to plain modern english for us poor foreigners ? I find it quite hard to read for non-natives, not even talking about cultural references.


This is ancient. It's so old I can't remember the last time I saw it, but I'm glad somebody remembered to submit it just for kicks.


"Also, contrary to the beliefs common among the more backward inhabitants of the Polluted Eastern Marshes, `NULL' does not have a pointer type, and must be cast to the correct type whenever it is used as a function argument."

Why? This seems like a waste of thought...


I had the same question. Also, I'm having trouble guessing what the Polluted Eastern Marshes would be here… IBM (New York)? Judging from the rest I wouldn't guess that IBM was too into C when it was written, but it's hard to tell. So … Japan? Nah…


The Polluted Eastern Marshes would be New Jersey. More specifically, Murray Hill, New Jersey, home of Bell Labs, and Unix.


Ahh, thanks!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: