The Ten Commandments for C Programmers (Annotated Edition)
lysator.liu.se
lysator.liu.se
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.
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.
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. ssize_t i;
#if SSIZE_MAX != INT_MAX
assert(i <= INT_MAX);
#endif /* SSIZE_MAX != INT_MAX */
f(i);the article is clearly rather dated, and a number of the points (as jws notes) are rather irrelevant now.
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.
> ... 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!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.
Very handy.
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.
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).
Ah, good point.
int *p = malloc(LOTS);
int v = read_from_network();
int o = read_from_network();
p[o] = v;
if that malloc fails, for example.(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.
Why? This seems like a waste of thought...