《C陷阱和缺陷》读书笔记-其三:预处理、连接、库函数、可移植性与建议
第四章 连接
1、连接器
编译器的责任是把C源程序转换成机器语言,连接器是将有编译器或汇编器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体。
2、声明和定义
两个具有相同名称的外部对象(全局变量)实际上代表的是同一对象,为避免此类的命名冲突,可以使用static修饰符。
如果一个函数仅仅被同一个源文件中的其他函数调用,就应该将其声明为static。
需要保证一个特定名称的所有外部定义在每个目标模块中都有相同的类型;为解决这个问题,可以使用头文件,即每个外部对象仅在一个头文件中声明,需要用到该外部对象的所有模块都应该包括这个头文件,定义该外部对象的模块也应该包括这个头文件。
第五章 库函数
1、尽量使用系统头文件。
2、在使用errno检测错误时,首先要检测调用库函数的返回值,当确认其执行失败后再检查errno,确认出错原因。
3、信号是真正意义上的异步,一个信号可能在C程序执行期间的任何时刻发生。信号处理函数,通常仅设置一个标志,期待主程序能够检查到这个标志,发现一个信号已经发生。
第六章 预处理器
1、预处理器在编程过程开始之前,对程序代码做必要的转换处理。
2、宏提供了一种对组成C程序的字、字符进行变换的方式,而不作用于程序中的对象。
3、建议在宏定义中把每个参数都用括号括起来,且整个结果表达式也应该用括号括起来;多条语句应该使用do-while格式。
4、需要确保宏中的参数没有副作用。
5、宏不能代替类型定义,类型定义时尽量使用typedef。
第七章 可移植性缺陷
1、整数的大小:short型整数不大于int型,int型整数不大于long型。一个int型整数足够大以容纳任何数组下标。
2、位移运算:如果被移位的对象是无符号数,那个向右移位时,空出的位是由0填充;如果是有符号数,则既可以用0填充又可以用符号位的副本填充;移位的计数必须严格大于或等于0,严格小于对象长度。
3、内存位置0:NULL指针不指向任何对象,仅用来赋值或者比较运算,其他目的使用NULL指针都是非法的。(若错误的写入,可能会覆盖操作系统的部分内容,造成彻底的灾难)
4、随机数的大小:ANSI C标准中定义了一个常数RAND_AMX,表示随机数的最大取值。
5、提高软件的可移植性,实际上是延长了软件的生命周期,实际上大多数工作是确保边界条件的正确性。
建议
1、不要说服自己相信“看起来肯定没问题的东西”
2、直截了当的表明意图:使用括号或者其他方式
3、考查最简单的特例:如输入数据为空或者只有一个元素
4、使用不对称边界
5、防御性编程:对程序用户和编译器实现不要有过多的假设。