Here’s a concise paper describing common C programming pitfalls by Andrew Koening (www.literateprogramming.com/ctraps.pdf) corresponding to be book with the same title.
As a reminder to myself, I’ll spend this page summarizing common mistakes and my history with it.
Here are the mistakes that I don’t make because of certain programming habits:
- Operator precedence: I use enough parenthesis to not rely on operator precedence
- Pointer dereferencing: I always do
*(p++)
instead of*p++
unless it’s idiomatic. for()
orif()
statements executing only first line: I always surround the block with{}
even if it’s just one line. Too often we need to inject an extra line and without {} it becomes a trap.- Undefined side effect order: I never do something like
y[i++]=x[i]
char* p, q
: very tempting since C++ style emphasize on pointer as a type over whether the variable is a pointer. I almost never declare multiple variables in one line.- Macro repeating side effects: use inline functions instead whenever possible. Use templates in C++.
- Unexpected macro associations: guard expressions with
()
. Usetypedef
.
Did these once before, adjusted my programming habits to avoid it:
- counting down in
for()
loop with unsigned running variable: I stick with signed running variables in general. If I’m forced to use unsigned, I’ll remind myself that I can only stop AFTER hitting 1, but not 0 (i.e. i=0 never got executed).
Haven’t got a chance to run into these, but I’ll program defensively:
- Integer overflow: do
a<b
instead of(a-b)<0
. Calculate mean by adding halfway length to the smaller number (i.e.(a+b)/2 == a + (b-a)/2
givena<b
). Shows up in binary search. - Number of bits to shift is always unsigned (i.e. -1 is a big number!)
What I learned from the paper:
stdio
buffer on stack (registered withsetbuf()
) freed before I/O flushed: use static buffer (or just make sure the buffer lives outside the function call).char
type might be signed (128 to 255 are -128 to -1) so it sign extends during upcast. Useunsigned char
go guarantee zero extend for upcasting.toupper()/tolower()
might be implemented as a simple macro (no checks, incorrect /w side effects)- Can index into a string literal:
"abcdefg"[3]
gives'd'
Mistakes that I usually make when I switch back from full-time MATLAB programming:
- Logical negation using
~
operator instead of ! operator.
Common mistakes I rarely make because of certain understanding:
- Forgetting to
break
at everycase
inswitch
block. It’s hard to forget once you’re aware of the Duff’s device. sizeof(a[])/sizeof(a[0])
after passing into a function does not give array length: hard to get it wrong once you understand that array (declared on stack) has meta-info that cannot be accessed beyond the stack level it’s initialized.