C语言缺陷与陷阱读书笔记(七)

可移植性缺陷##

7.1 应对C语言标准变更###

随着语言标准的不断升级,使得程序越来越容易编写,而且不大容易出错。但是也同时让这些程序无法在较旧的编译器工作。要解决这个问题,需要充分考虑程序的向下兼容。

7.2 标识符名称的限制###

为了保证程序的可移植性,谨慎地选择外部标识符的名称是重要的。不然可能会引起灾难性后果。

7.3 整数的大小###

1.一个普通(int类型)整数足够大以容纳任何数组下标。
2.字符长度由硬件特性决定。

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

符号拓展只针对将字长小的数据赋给字长大的数据,若是字长大的数据赋给字长小的数据,取低位即可.
下面代码为例:

#include<stdio.h>

int main(void)
{
unsigned char ch1=0XFF;
char ch2=0XFF;
char ch3=0X73;
int a=ch1;
int b=ch2;
int c=ch3;
printf("%d\n%d\n%d\n",a,b,c);
return 0;
}

运行结果为:

255
 -1
115

逐个解析:

  1. .由于ch1,ch2,ch3都是char类型变量,只占一个字节,但是由于ch1是无符号的,因此ch1看作无符号数据来处理,当其转换为int类型时,其高位用0来填充,故结果为255.
  2. ch2不是无符号型数据,又因为其二进制表示的最高位为1,所以高位均填充为1,即填充后为0X FF FF FF FF,结果为-1.
  3. ch3的最高为0,填充c的高位用0填充,因此c的值为0x 00 00 00 73

7.5 移位运算符###

  1. 在向右移位时,空出的位是由0填充,还是由符号位的副本填充与具体的C语言实现有关,如果被移位的为无符号数,则空出的位将被0填充,如果被移位的对象是有符号数,那么既可能是由0来填充,或者由符号位的副本来填充。
  2. 移位计数的取值范围,如果被移位的对象长度为n位,那么移位计数必须大于或等于0,而严格小于n。
  3. 有符号整数的向右移位运算符也不等同于除以2的某次幂,并且移位运算的运行比除法运算的速度快得多。

7.6 内存位置0###

null指针并不指向任何对象,除非用于赋值或者比较运算,其他使用null指针都是非法的。
要解决这类问题需要把问题程序移到不允许读取内存位置为0的机器上运行。

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

在早期的realloc函数的实现要求重新分配内存区域必须首先被释放。因此一些较老的C程序是首先释放某块内存,然后再重新分配这块内存。

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

void printnum (long n,void (*p)())
{
if (n<0)
{
    (*p) ('-');
    n=-n;
}

if (n>=10)
    printnum (n/10,p);
    (*p)((int) (n%10) +'0');

}

上面这段代码是先检查n是否为负,如果是负数就打印一个符号然后再将n反号。 然后再检查n是否大于10,如果大于10就递归调用printnum函数来打印n的10进制表示中除了最后一位以外
的所有数字。最后打印其末位数字。为了是*p正确地处理参数,将n%10的值转换为int类型。

这个程序却存在可移植性的几个问题:

  1. (*p)((int) (n%10) +'0');
    这行代码实现的前提是机器的字符集中数字是按顺序排列,没有间隔的。否则就会出现问题。解决办法是使用一张代表数字的字符表,因为一个字符串常量可以用来表示一个字符数组。所以应该改为:

    (*p)("0123456789" [n%10]);
    
  2. n=-n;
    这个赋值操作可能会出现溢出问题,由于基于2的补码的计算机一般允许表示的负数取值范围要大于整数的取值范围,因此当n为负数且足够大时,当其转换为正数时会发生溢出。解决办法是保证不将n转换为对应的正数。

     void printneg (long n,void (*p)())
     {
    
    
     if (n<=10)
          printneg (n/10,p);
    
      (*p) ("0123456789"[-(n%10)]);
    
    
     }
    
    
     void printnum (long n,void (*p)())
     {
    
     if (n<0)
     {
    
         (*p) ('-');
         printneg(n,p);
    
     }
     else
         printneg(-n,p);
    
     }
    

上面代码保证了对于n的操作始终为负数或者零的算术操作。这样也就解决了溢出的问题了。但是由于整数的除法运算中一个操作数为负时,它的行为表现与具体的实现有关。因此当n为负数时,n%10的结果有可能是负数。导致没有对应的字符串常量可以与其配对,继而程序出错。要解决这个问题,需要创建两个临时变量,来分别保存商和余数。检查余数是否在合理的范围之内。代码如下

void printneg (long n,void (*p)())
{
long q;
int r;

q=n/10;
r=n%10;

if (r>0)
{
    r-=10;
    q++;
}

if (n<=10)
    printneg (q,p);
(*p) ("0123456789"[-r]);
}

posted on 2015-09-27 07:36  陆游君语  阅读(967)  评论(0编辑  收藏  举报

导航