(1) Values returned by mathematical functions on domain errors are implementation defined.
The best way of handling such domain errors is to ensure that they never arise at all. e.g Test for positive value before taking square root. Assuming that the compiler will return a certain value when the number is negative is a silly mistake.
(2) The minimum guaranteed value for the char type is zero.
This means that it is up to the implementation to have chars behaving as signed or unsigned char. To obtain the minimum value for your chars use CHAR_MIN defined in limits.h.
(3) Side effects are also silly
It is unsafe to use the same variable more than once in an expression if evaluating that expression changes the variable and the new value could affect the result of the expression.
Be careful when writing long expressions involving increment/decrement operators or expressions which call functions by reference.
Even x = x++; is unsafe.
(4) Don't skip the precedence tables.
Precedence tables are often skipped by people when reading introductory books but sometimes they can come back and smack you with the silly tag. If you miss that == (and !=) have higher precedence than ?: then you might expect c = (1?0:1 == 1?0:1); to assign the value 1 to c. Your reasoning would probably be that whatever is to the left of == is the same as what is to its right so the result should be true and c be one. That kind of reasoning is of course quite silly. The precedence table shows that the expression is evaluated as c = (1?0:(1 == 1)?0:1); with the inner brackets evaluated first then from left to right leaving c with a value of 0.
Association is also important e.g while ++ and * have equal precedence, they associate right to left meaning that *p++ will be evaluated as *(p++).
(5) Always use function prototypes.
If you don't then they are assumed to be taking the default type which is int. You might start to think that if you know the defaults then you should be able to assume them and not have to explicitly specify them. Those are very silly thoughts and should be immediately dismissed from the mind. Also cast any function arguments to the expected type if they are not that type already.
(6) The order of evaluation of function arguments is not defined.
That means that in
Expand|Select|Wrap|Line Numbers
- int a () {
- printf("a\n");
- return 1;
- }
- int b () {
- printf("b\n");
- return 2;
- }
- int c () {
- printf("c\n");
- return 3;
- }
- int sum (int a, int b, int c) {
- return a + b + c;
- }
- int main(int argc, char** argv) {
- printf("%d", sum(a(), b(), c()));
- return (EXIT_SUCCESS);
- }
the only guaranteed output is 6 (the sum) not the order in which a,b or c are printed.
(7) sizeof pointer to type T is the size of only one T element.
In particular, in
Expand|Select|Wrap|Line Numbers
- T arr[10];
- T *p = arr;
The size of sizeof is also implementation defined and size_t (defined in stdlib.h) or unsigned long should be used to hold its value.
(8) main must return an int.
You don't call main in your programs, do you? So who calls it? How does he know how you have defined your main if he is calling it? The answers are simple. The guy who calls your main assumes that your main returns an int. Usually the caller will use that returned value to determine the success status of the program. If you don't give him that, he is going to get garbage and use that for whatever reed dancing he does with the returned value.
(9) Mixing signed and unsigned types with closed eyes.
The man who declares an unsigned type must slap himself awake every time he uses it in an expression with other types. Indeed anyone who writes any expression involving mixed types must be on guard. The stories about goblins lurking around programs collecting bits from expression evaluations are true and should not be deemed as silly.
(10) Array bounds. Need I say more?
If any of your functions take an array as argument, then pass that array's length as well. Avoid any functions which accept an array without also taking its length. In particular never use gets().