类型的自动转换
Operands with different types get converted when you do arithmetic. Everything is converted to the
type of the floatiest, longest operand, signed if possible without losing bits.
The value preserving approach (ANSI C) says that when you mix integral operand types like this, the
result type is signed or unsigned depending on the relative sizes of the operand types.
The defined variable TOTAL_ELEMENTS has type unsigned int (because the return type
of sizeof is "unsigned"). The test is comparing a signed int with an unsigned int quantity. So
d is promoted to unsigned int. Interpreting -1 as an unsigned int yields a big positive
number, making the clause false. This bug occurs under ANSI C, and under K&R C if
sizeof() had an unsigned return type in a given implementation.
Avoid unnecessary complexity by minimizing your use of unsigned types. Specifically,
don't use an unsigned type to represent a quantity just because it will never be negative
e.g., "age" or "national_debt").
Use a signed type like int and you won't have to worry about boundary cases in the
detailed rules for promoting mixed types.
Only use unsigned types for bitfields or binary masks. Use casts in expressions, to make all
he operands signed or unsigned, so the compiler does not have to choose the result type.
Chapter 2
Why Language Features Matter
The one "l" NUL ends an ASCII string,
The two "l" NULL points to no thing.
Switches Let You Down with Fall Through
Switch has several problems, one of which is that it is too relaxed about what it accepts in the cases.
For example, you can declare some local storage by following the switch's opening curly brace with a
declaration.
It is always the case in C that where you have some statements opening a block
{
statements
you can always add some declarations in between, like this:
{
declarations
statements
You might use this if allocating memory was expensive, and hence avoided if possible. A
compiler is free to ignore it, though, and allocate the space for all local blocks on calling a
function. Another use is to declare some variables whose use is really localized to this
block.
Another problem is that any statements inside a switch can be labelled and jumped to, allowing
control to be passed around arbitrarily:
switch (i) {
case 5+3: do_again:
case 2: printf("I loop unremittingly /n"); goto do_again;
default : i++;
case 3: ;
}
Perhaps the biggest defect in the switch statement is that cases don't break automatically after the
actions for a case label. Once a case statement is executed, the flow of control continues down,
executing all the following cases until a break statement is reached