《C陷阱与缺陷》读书笔记6、7

第六章 预处理器

 

6.1  不能忽视宏定义中的空格

 

        #define f (x) ((x) – 1)      ≠    #define f(x)( (x) – 1)

 

6.2  宏并不是函数

 

        宏定义中的所有括号,它们的作用是预防引起与优先级有关的问题。

 

        最好把每个参数都用括号括起来

        #define abs(x)x>0?x:-x

        abs(a-b) 会被展开为a-b>0?a-b:-a-b

        -a-b相当于(-a)-b,而不是期望的-(a-b)

 

        整个结果表达式也应该用括号括起来,以防止当宏用于一个更大一些的表达式中可能出现的问题。

        abs(a)+1展开后的结果为: a>0?a:-a+1

        期望的是-a,而不是-a+1。

 

        即使宏定义中的各个参数与整个表达式都被括号括起来,也仍然还可能有其他问题。比如,一个操作数如果在两处被用到,就会被求值两次。

 

        使用宏的另一个危险是,宏展开可能产生非常庞大的表达式,占用的空间远远超过了编程者所期望的空间。

 

6.3  宏并不是语句

 

6.4  宏并不是类型定义

 

        #define T1struct foo *

        Typedef structfoo *T2;

        试图声明多个变量:

        T1 a, b;

        T2 a, b;

        第一个声明被扩展为:

        struct foo * a,b;

 

第七章  可移植性缺陷

 

7.1  应对C语言标准变更

 

7.2  标识符名称的限制

 

        ANSI C标准所能保证的知识,C实现必须能够区别出前6个字符不同的外部名称。而且,整个定义中并没有区分大写字母与其对应的小写字母。

 

        两个函数的名称分别为print_felds与print_float,这样的命名方式就不恰当;同理,使用State与STATE这样的命名方式也不理智。

 

7.3  整数的大小

 

        C语言中为编程者提供了3种不同长度的整数:short、int和long。

        C语言的定义中对各种不同类型整数的相对长度作了一些规定:

        3种类型的整数其长度是非递减的。对于一个特定的C语言实现来说,并不需要实际支持3种不同长度的整数,但可能不会让short型整数大于int型整数,而int型整数大于long型整数。

        一个普通(int类型)整数足够大以容纳任何数组下标。

        字符长度由硬件特性决定。

 

7.4  字符是有符号整数还是无符号整数

 

        如果编程者关注一个最高位是1的字符其数值究竟是正还是负,可以将这个字符声明为无符号字符(unsigned char)。这样,无论是编译器,在将该字符转换为整数时都只需要将多余的位填充为0即可。而如果声明为一般的字符变量,那么在某些编译器上可能会作为有符号数处理,在另一些编译器上又会作为无符号数处理。

        与此相关的一个常见错误是:如果c是一个字符变量,使用(unsigned)c就可得到与c等价的无符号整数。这是会失败的,因为在将字符c转换为无符号整数时,c将首先被转换为int型整数,而此时可能得到非预期的结果。

 

7.5  移位运算符

 

        使用移位运算符的两个问题:

        1.       在向右移位时,空出的位是由0填充,还是由符号位的副本填充?

        2.       移位计数(即移位操作的位数)允许的取值范围是多少?

        第一个问题的答案是:如果被移位的对象是无符号数,那么空出的位将被0填充。如果被移出的对象是有符号数,那么C语言实现既可以用0填充空出的位,也可以用符号位的副本填充空出的位。

        第二个问题的答案是:如果被移位的对象长度是n位,那么移位计数必须大于或等于0,而严格小于n。

 

        需要注意的是,即使C实现将符号位复制到空出的位中,有符号整数的向右移位运算也并不等同于除以2的某次幂。(-1)>>1,整个操作的结果一般不可能为0,但是(-1)/2在大多数C实现上求职结果都是0。

 

7.6  内存位置0

 

        null指针并不指向任何对象。因此,除非是用于复制或比较运算,出于其他任何目的使用null指针都是非法的。

 

        在所有的C程序中,误用null指针的效果都是未定义的。然而,这样的程序有可能在某个C语言实现上“似乎”能够工作,只有当该程序转移到另一台机器上运行时才会暴露出问题来。

 

7.7  除法运算时发生的截断

 

        假定a除以b,商为q,余数为r:

        q = a/b;

        r = a%b;

        a、b、q、r之间维持怎样的关系?

        1. 最重要的一点,q*b+r == a,因为这是定义余数的关系。

        2. 如果改变a的正负号,我们希望这会改变q的符号,但这不会改变q的绝对值。

        3. 当b>0时,希望保证r>=0且r<b。

        这三条性质并不可能同时成立。因此,C语言与其他语言在实现整数除法截断运算时,必须放弃上述三条原则中的至少一条。大多数选择放弃第三条。而要求余数与被除数的正负号相同。

        然而,C语言的定义只保证了性质1,以及当a>=0且b>0时,保证|r|<|b|以及r>=0.

 

7.8  随机数的大小

 

7.9  大小写转换

 

7.10  首先释放,然后重新分配

 

7.11  可移植性问题的一个例子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值