《编写高质量代码-改善C程序代码的125个建议》读书笔记-1数据

容易写bug的点:

进行判断的数据类型不一样;

函数返回值和定义的数据类型不一致。例如ret=strlen(),strlen的返回值和ret数据类型不一致

1- 回绕溢出问题

当我们的数据超出数据类型表示范围,将发生回绕。例如unsigned char表示范围为0-255,当数据unsigned char index=255; result = index+1将发生回绕,result=0.

回绕的危害行为主要发生在:索引、内存分配、循环比较、判断等。

不知道表述是否准确。。。

发生回绕的数据运算有:

1.1- 显式声明为signed char或unsigned char的类型来执行算术运算


因为不显式声明是否有符号,将由编译器决定是否加符号

1.2- 使用rsize_t或size_t类型来表示一个对象所占用空间的整数值单位

size_t、rsize_t常用语表示无符号数,适用于索引、循环比较、数据大小等场景使用。

但是不同平台下size_t表示的数据不同,例如size_t在不同平台可能表示为unsigned int;unsigned long int; unsigned long long int

注:sizeof的返回值类型类size_t

确保入参和其比较值数据类型相同

订正:

1.3- 使用显式声明为signed char或unsigned char的类型来执行算术运算

订正:

注:如果想让编译器自动处理是否为有符号、无符号的数,需要将数字表示为有符号、无符号的交集, 即0-127之间 

1.4- 无符号数和有符号数数据运算,会将有符号数自动转换为无符号数

无符号数和有符号数数据运算,会将有符号数自动转换为无符号数!!!!

示例:

订正:

总归来说,数学运算的数字一定要数据类型相同,否则会发生错误。

注:sizeof的返回值类型类size_t 

1.5- 防止无符号数的回绕

无符号数表示的范围为0-xxx_MAX,当无符号数超过最大表示范围时将发生回绕从0开始,并递增。

案例1:

订正:

案例2:

下面这个案例是刻意造的,将int数据转化为unsigned short,将发生回绕。例如i= unsigned_short_max+95;

s=i=95;

满足判断条件if(s<100)

接下来的memcpy(buff,xxx, unsigned+95)将发生异常,因为buff为100,小于unsigned+95;另外把unsigned+95的缓冲大小进行拷贝超出我们的软件预期设计,引发异常。

 案例3:

下面这个案例,在malloc时使用len*sizeof(int)作为分配内存大小,可能发生回绕。例如len=0x40000000; sizeof(len)=4; 0x40000000*4=0发生回绕。

导致我们分配了0个内存大小,但是下面for循环却在处理0x40000000个内存块,导致我们这些内存里的数据被改写,引发异常。

订正:

数学运算前进行判断是否发生回绕。

2- 数据类型转换必须做范围检查

C语言中数据类型转换有隐式转化和显示转换。

隐式转换:

a- 一般算数转换

通过某些运算符将操作数的值从一种类型自动转换成另为一种类型,这一规则为“由低级向高级转换”

若参与运算的变量类型不同,则先将变量的类型转换成同一类型,然后再进行运算。例如,int类型的变量和long类型的变量参与运算时,则会先把int类型的变量转成long类型,然后再进行运算。

这里需要特别注意的是,所有的浮点运算都是以双精度进行的,即使表达式中仅含float单精度变量,也要先将其转换成double类型后再进行运算。同时,如果char类型的变量和short类型的变量参与运算,则必须先转换成int类型

b- 输出转换 

输出的操作数类型与输出的格式不一致时所进行的数据类型的转换。如下面的示例代码所示:

c- 赋值转换

在赋值运算过程中将赋值运算符右侧的操作数类型转换成左侧操作数据的类型

d- 函数调用转换

当实参类型和形参类型不一致时数据所进行的转换。

显示转换

a- 强制性数据类型转换

它是将一种类型的数据强制转换成为另一种数据类型。

char num = 5;
int tmp = (int)num;

b-  利用C语言提供的标准函数转换

2.1- 整数转化为新类型时必须做范围检查

示例: 无符号转化为有符号

从一种无符号类型转换为一种有符号类型时,就可能发生数据的高位被截断而导致数据丢失,或者符号位丢失,所以在转换之前要对取值范围进行验证。

示例:有符号转化为无符号 

示例:从高级向低级转换

3- 使用严格定义的数据类型

不同编译器对于char、int等数据的解释不同,如果不想出错就自己限制数据是有符号还是无符号的吧。

4- 尽量不要在可重入函数中使用静态(或全局)变量

可重入函数是指:函数可以由多于一个的任务并发使用,而不必担心数据错误

 由于下面函数中使用静态变量sum,sum存储在静态区,变量值具有记忆性,所以函数不可重入。

可重入的函数,那么一定要尽量避免使用static变量与全局变量,能不用则尽量不用。

5-  尽量少使用全局变量

全局变量使用有诸多弊端:

a- 函数在执行时依赖其所在的外部变量。在程序设计时,我们要求模块的功能单一,各模块之间的相互影响尽量少,而用全局变量很显然是不符合这个原则的。移植性、可读性变差

b- 使用的全局变量过多,会降低程序的清晰性,我们往往难以清楚地判断出每个瞬时各个外部变量的值。

c- 使用全局变量、静态全局变量与静态局部变量的函数时,需要考虑重入问题。

6- 尽量使用const声明值不会改变的变量

使用const变量除了可以确保变量值不被修改之外,同时它还可以节省存储空间,避免不必要的内存分配。

通常,编译器并不为普通const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,从而提高效率。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值