这本书不是批判,而是指出易犯错误,防止编程中的陷阱和障碍。
1、易犯错误类型归纳:
(1)词法分析;(2)语法细节问题;(3)语义细节;(4)库函数的误用;(5)预处理器;(6)可移植性;(7)预防性程序设计;
2、运算符优先级:
(1)任何一个逻辑运算符的优先级低于任何一个关系运算符;
(2)移位运算符的优先级比算术运算符要低,但是比关系运算符要高;
例如:r = high << 4 + low; 等价于 r = high << ( 4 + low );
(3)运算符== 和 != 的优先级要低于其他关系运算符;
3、指针与数组:
(1)int calendar [12][31]; ----> calendar是一个有着“12个数组类型元素”的数组,它的每个数组类型元素又是一个有着“31个整型元素“的数组。
(2)int *p; p = calendar[4]; ----> 指针p指向了数组calendar[4]中下标为0 的元素。
(3)int i; i = calendar[4][7]; 等价于 i = *( calendar[4] + 7 ); 等价于 i = *( ( calendar + 4) + 7 );
(4)int (*month)[31]; ----> 声明了 *month 是一个拥有31个整型元素的数组;
(5)month = calendar; ----> month将指向数组calendar的第一个元素,也就是数组calendar数组的12个有着“31个整型元素“的数组类型元素之一;
4、static防止命名冲突:
我们可以在多个源文件中定义同名的函数wei_global_func(),只要所有的函数wei_global_func()都被定义为static,或者仅仅只有其中一个函数wei_global_func()不是static。也就是说,为了避免可能出现的命名冲突,如果一个函数仅仅被同一个源文件中的其它函数使用,那么我们就应该声明该函数为static。(全局变量亦如此)
5、函数printf 和 scanf:
我们希望从标准输入设备读入5个数,在标准输出设备上输出5个数:0 1 2 3 4 5。但是。。。
6、宏函数:
(1)不能忽视宏定义中的空格:
例如:
#define f(x) ( (x) - 1 ) -----> f(x)代表( (x) - 1 ) ;
#define f (x) ( (x) - 1 ) -----> f(x)代表( x ) ; 注意 f 和 ( x ) 之间有空格!
(2)还要注意宏函数是直接替换!最好在宏定义中把每个参数都用括号括起来,最好也把整个表达式用括号括起来。
例如:
#define abs(x) ( ( (x) >= 0 ) ? ( x ) : - ( x ) )
求abs( a - b) 时,((a - b) > 0 ? (a - b) :-(a - b))。正确!
假设写成: #define abs(x) x >= 0 ? x : - x
求abs( a - b) 时,a - b > 0 ? a - b :- a - b。错误!
(3)还要注意宏函数中的++ 和 -- ,在代码中宏函数是直接替换,有可能会++ 和 --好几次(看操作数用到几次)!总之要记住的就是“宏函数是直接替换”。(递增运算符和递减运算符的副作用)
(4)如果宏展开可能产生非常庞大的表达式,占用的空间远远超过了所期望的空间,那么就要弃用宏函数!