编写优质无错C程序秘诀!_第1页
编写优质无错C程序秘诀!_第2页
编写优质无错C程序秘诀!_第3页
编写优质无错C程序秘诀!_第4页
编写优质无错C程序秘诀!_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、编写优质无错C程序秘诀!经验谈 分类 : 其他    发布时间 : 2007-06-20    来源 : A 编写优质无错C程序秘诀!经验谈 编写优质无错C程序秘诀!经验谈 这里我将陆续给大家载出我在以前学习和编写c代码时遇到的问题和解决方法、学习的心得,有些是经过查询一些曾经参加微软microsoft的开发小组的老程序员的书籍和资料后提供给大家! 首先,当发现错误时,要不断就以下两个问题追问自己的结果:1、怎样才能自动地查出这个错误?2、怎样才能避免这个错误? 关于错误:错误可以分为两类:1、开发某一功能时产生的错误。2、程序员认为该

2、功能已经开发完成之后仍然遗留在代码中的错误。 第一种错误好解决,可以把编译器可以设置的警告等级开关打开,以及语法检查来排除;逻辑错误也可以使用跟踪手段来排除。跟踪逻辑错误就相对麻烦一些,要消除这些麻烦就要养成一个好的编程习惯和方法。第二种错误时非常隐蔽的,需要长期的实践和经验在其中,还要对c语言具有深刻的了解才能够提高上来,这里就是要告诉大家一些这样的事情,通过代码解说来阐明具体事实。 以下的文章里,实际上有许多是微软 microsoft 的老程序员开发 word 和 excel 的经验之谈,这也是我当初学习他们的经验时的体会和材料的

3、总结和整理。 总之,这些对于在c道路上前进的人们是非常重要的,不敢独占,先拿出来以供大家享受   (第一个问题)考虑自己所用的语言和编程环境?使空语句明显化!充分利用语言的特性和编程环境,把所有环境下的调试报错等级开关都打开,注意使用语言的保留字,例如下面的两段程序对比: /*复制一个不重叠的内存块*/void *memcpy(void *pvto, void *pvfrom,size_t size)    byte *pbto = (byte *)pvt

4、o;    byte *pbfrom = (byte *)pvfrom;    while(size- > 0);        *pbto+ = *pbfrom+;    return (pvto); 推荐精选从以上缩进格式可以看出,while后的分号肯定是一个错误。但编译器认为这是一个合法的语句,允

5、许循环体为空语句。报警开关都打开时,大多编译器都能够报出这一错误。但需要用空语句时,最好实用null(大写)明确出来: char *strcpy(char *pchto, char *pchfrom)    char *pchstart = pchto;    while(*pchto+ = *pchfrom+)        null;/*此处n

6、ull大写*/    return (pchstart); 这样,编译器编译程序接受显式的null语句,把隐式空语句自动地当做错误标出。 (第二个问题)无意的赋值。例如: if(ch = 't')    expandtab(); 有些编译器允许在程序&&和|表达式以及if、for和while中直接使用赋值的地方禁止简单赋值,如果以上五种情况将=偶然地键入为=号,就会报错。 while(*pchto+ = *pchfrom+) 

7、;   null; 编译程序就会产生警告信息,为了防止这种情况出现,可以这样做: while(*pchto+ = *pchfrom+) != '0')    null; 这样做的结果由两个好处:1、现在的编译器不会为这种冗余的比较产生额外的代码和开销,可以将其优化掉。2、可以少冒风险,尽管以上两种都合法,但这是更安全的用法。   推荐精选(第三个问题)参数错误:例如:fprintf(stderr, 'unable to o

8、pen file %s.n',filename);.fputc(stderr,'n');这个程序看上去好像没有问题,实际上fputc的参数顺序错了。幸好ansi c提供了函数原型,在编译时自动查出这些错误。 ansi c标准要求每个库函数都必须有原型,stdio.h中可以查到:int fputc(int c, file *stream);如果在程序文件头里给出了原型,这类错误就可以检查出。 ansi c虽然要求标准库函数必须有原型,但并不要求用户编写的函数也必须有原型。可以有,也可

9、以没有。有些程序员经常抱怨对函数的原型进行维护,如果没有原型,就不得不依靠传统的测试方法来查出程序中的调用错误,大家可以扪心自问:究竟哪个更重要?利用原型可以生成质量更好的代码。ansi c标准使得编译程序可以根据原型信息进行相应的优化。 有这样的名言:投资者与赌徒之间的区别在于投资者利用每一次机会,无论它是多么小,去争取利益;而赌徒则只靠运气。我们应该将这一概念同样应用于编程活动。把所有的警告开关都打开,除非有极好的理由才不这样做! (原则一)如果有单元测试,就进行单元测试。你认识那个程序员宁愿花费时间去跟踪排错,而不是编写新的代码?肯定有这样的程序员,但我至今还没有见到一个。当你

10、写程序时,要在心中时刻牢记着假想编译程序这一概念,这样就可以毫不费力或者直费很少力气利用每个机会抓住错误。如果想要快速容易地发现错误,就要利用工具的相应特性对错误进行定位。错误定位的越早,就能够越早地投身于更有趣的工作。努力减少程序员查错的技巧。可以选择编译程序的环境来实现。高级的编码方法虽然可以查出或减少错误,但它们也要求程序要有较多的技巧,因为程序员必须学习这些高级的编码方法。   (原则二)自己设计并使用断言。    利用编译器自动查错固然好,但实际上只是很少一部分。如果排除掉了程序中的所有错误,大部分时间程序会正确工作。 推荐精选看一下

11、下列代码:strcopy = memcpy(malloc(length),str,length);     该语句在多数情况下会工作的很好,除非malloc的调用产生失败。一旦产生,就会给memcpy返回一个null指针,而memcpy处理不了null指针,这样的错误产生,如果在交付用户之前将导致程序的瘫痪。但如果交付了用户,那用户就一定“走运”了。 解决方法:对null指针进行检查,如果为null,就给出一条错误信息,并终止memcpy执行。ee /*拷贝不重叠的内存块*/void memcpy(void *pv

12、to, void *pvfrom, size_t size)    void *pbto = (byte *)pvto;    void *pbfrom = (byte *)pvfrom;    if(pvto = null | pvfrom = null)   

13、60;        fprintf(stderr, 'bad args in memcpy!n');        abort();         while(size- > 0)       

14、 *pbto+ = *pbfrom+;    return(pvto);     只要调用时错用了null指针,这个函数就会查出来。但测试的代码增加了一倍,降低了执行速度,这样“越治病越糟”,还有没有更好的方法?    有,利用c的预处理程序!    这样就会保存两个版本。一个整洁快速,用于交付用户;另一个臃肿缓慢(包含了额外的检查),用于调试。这样就要同时维护同一个程序的两个版本,利用c的预处理程序有条件

15、地包含相应的部分。 例如:只有定义了debug时,才对应null指针测试。void memcpy(void *pvto, void *pvfrom, size_t size)    void *pbto = (byte *)pvto;    void *pbfrom = (byte *)pvfrom;     #ifdef de

16、bug        if(pvto = null | pvfrom = null)                    fprintf(stderr, 'bad args in memcpy!n');

17、            abort();             #endif 推荐精选    while(size- > 0)        *pbto+ = *pbfrom+;&#

18、160;   return(pvto); 这样,调试编译时开放debug,进行测试程序和找错;交付用户时,关闭debug后进行编译,封装之后交给经销商。    这种方法的关键是保证调试代码不在最终产品中出现。 那么还有没有比以上两种更好的方法,有!下次再讲。   (准则二续)利用断言进行补救。     实际上,memcpy中的调试代码编的非常蹩脚,喧宾夺主。他能产生好的效果,这无疑,但许多程序员不会让他这样存在的,聪明的程序员会让调试代码隐藏在断言assert中。 

19、   assert是个宏,定义在头文件assert.h中,每个编译器都自带。前面的程序完全可以使用assert来处理,看一下下面代码,把7行减为了1行代码。 void memcpy(void *pvto, void *pvfrom, size_t size)    void *pbto = (byte *)pvto;    void *pbfrom = (b

20、yte *)pvfrom;     assert(pvto != null  &&  pvfrom != null);     while(size- > 0)        *pbto+ = *pbfrom+;    return(pvto);

21、这里要强调的是:assert是个只有定义了debug才起作用的宏,如果其参数的计算结果为假,就中止调用程序的执行。     当然程序编制也可以编制自己的断言宏,但要注意不要和assert冲突,因为assert是全局的。举个例子: 推荐精选先定义宏assert:#ifdef debug    void _assert(char *,  unsigned);  /*自定义断言函数的函数原型*/    #defi

22、ne assert(f)    if(f)        null;    esle        _assert(_file_ ,  _line_ );#else    #define  assert(f)   

23、60;null#endif     从上述我们可以看到,如果定义了debug,assert将扩展为一个if语句。    当assert失败时,他就是用预处理程序根据 _file_ 和 _line_ 所提供的文件名和行号参数调用 _assert。 _assert在标准错误输出设备stderr上打印一条错误信息,然后中止: void  _assert(char *strfile,  unsigned 

24、uline)    fflush(stdout);    fprintf(stderr, 'nassertion failed: %s, line %un', strfile, uline);    fflush(stderr);    abort(); 程序中的相关函数,大家可以查阅头文件帮助来了解,这里就不在详述了。下一讲:使用断言对函数参数确认。

25、   (准则二  续二)使用断言对函数参数确认。 掌握原则为:“无定义”就意味着“要避开”。     读一下ansi c的memcpy函数的定义,最后一行这样说:“如果在存储空间相互重叠的对象之间进行了拷贝,其结果无意义。”那么当使用相互重叠的内存块调用该函数时,实际上在做一个编译程序(包括同一编译程序的不同版本),结果可能也不同的荒唐的假定。    对于程序员来说,无定义的特性就相当于非法的特性,因此要利用断言对其进行检查。 通过增加一个验证两个内存块决不重叠的断言,可以把

26、memcpy加强:void memcpy(void *pvto, void *pvfrom, size_t size)    void *pbto = (byte *)pvto;    void *pbfrom = (byte *)pvfrom; 推荐精选    assert(pvto != null 

27、60;&&  pvfrom != null);    assert(pbto >= pbfrom+size  |  pbfrom >= pbto+size);     while(size- > 0)        *pbto+ = *pbfro

28、m+;    return(pvto);    从今以后,在编程时,要经常停下来看看程序中有没有使用了无定义的特性。如果使用了,就要把它从相应的设计中去掉,或者在程序中包含相应的断言,以便在使用了无定义的特性时,能够向程序员发出通报。   (须注意问题一)     前面所述的做法,为其他的程序员提供代码库(或操作系统例如各个厂家的编译器)时显得特别重要。如果为他人提供过类似的库(或者自己使用自己以前编过的库时,或者使用了不同厂家提供的库时),就应该知道当程序员试图得

29、到所需要的结果时,就会利用各种各样的无定义特性。更大的挑战在于改进后新库的发行,因为尽管新库与老库完全兼容,但总有半数的应用程序在试图使用新库时会产生瘫痪现象。问题在于新库在其“无定义的特性”方面,与老库并不100%兼容。    明白了这些,在编程时,就要考虑程序的移植性、兼容性、容错性、安全性、可发行性、商品性等等方面。而不是说在一个编程环境下能够实现功能就万事大吉了。程序员的道路不知是停留在编程的语法学习、技巧、实现功能上。要全面、全方位考虑所编制的程序的有可能造成的后果。    各个厂家的编译器都有所不同,一

30、个厂家的编译器版本不同时,特性也不同。要想很好的编程,这些都是需要了解的,去尽量了解这些特性,才能真正学到编程。才能提高编程效率。有时会出现这样的情况。看人家的代码,感觉到非常傻,自以为很聪明,实际上是自己错误,因为人家考虑的更加广泛,考虑的更多,实际上那样的代码特别具有可移植性和容错性。只是自己的思想受到了局限,只从一个角度来看问题造成的。劝告大家:千万不要夜郎自大!   大呼“危险”的代码:    我们再谈谈memcpy中的重叠检查断言。对于上面的重叠检查断言:    assert(pbto 

31、;>= pbfrom + size | pbfrom >= pbto + size);    假如在调用memcpy时这个断言测试的条件为真,那么在发现这个断言失败了之后,如果你以前从来没有见过重叠检查,不知道它是怎么回事,你能想到发生的是什么差错吗?但这并不是说上面的断言技巧性太强、清晰度不够,因为不管从哪个角度看这个断言都很直观。然而,只管并不等于明显。    很少比跟踪到了一个程序中用到的断言,但却不知道该断

32、言的作用这件事更令人沮丧的了。你浪费了大量的时间,不是为了排除错误,而只是为了弄清楚这个错误到底是什么。这还不是事情的全部,更有甚者,有的程序员偶尔还会设计出有错的断言。所以如果搞不清楚相应的断言检查的是什么,就很难知道错误是出现在程序中,还是出现在断言中。解决这个问题的办法,是给不够清晰的断言加上注解即可。这是显而易见的事情,但令人惊奇的是很少有程序员这样做。为了使用户避免错误的危险,程序员经历了各种磨难,但却没有说明危险到底谁什么。程序员不理解的断言也会被忽视。在这种情况下,程序员会认为相应的断 言是错误的,并把它们从程序中去掉。因此,为了使程序员能够理解断言的意图,要给不够清楚

33、的断言加上注解。    如果在断言中的注解中还注明了相应错误的其他可能解法,效果更好。例如在程序员使用相互重叠的内存块调用memcpy时,就是这样做的一个好机会。程序员可以利用注解指出此时应该使用memmove,它不但能够正好完成你想做的事情,而且没有不能重叠的限制:    /*内存块重叠吗?如果重叠,就使用memmove*/    assert(pbto >= pbfrom + size | pbfrom&#

34、160;>= pbto + size);    在写断言注解时,不要长篇大论。一般的方法是使用经过认真考虑过的简短问句,它可以比用一整段的文字系统地解释出每个细节的指导性更强。但要注意,不要在注解中建议解决问题的办法,除非你能够确信它对其他程序员确有帮助。做注解的人当然不想让注解把别人引入歧途。 推荐精选不要浪费别人的时间详细说明不清楚的断言 断言不是用来检查错误的 当程序员使用断言时,有时会错误地利用断言去检查真正的错误,而不去检查非法的情况。看一下下面函数strdup中的两个断言:/*strdup-为字符串分配一个

35、副本(不是拷贝一个副本,是分配)*/char *strdup( char *str)    char *strnew;    assert( str != null );    strnew = ( char *)malloc( strlen(str) + 1 );    ass

36、ert( strnew != null);    strcpy( strnew,  str);    return(strnew);第一个断言的用法是正确的,它被用来检查该程序正常工作时,绝对不应该发生的非法情况第二个断言的用法相当不同,它测试的是错误情况,是在其最终产品中肯定会出现,并且必须对其进行处理的错误情况也就是说:断言是用来检查非法情况的,而不是测试和处理错误的。   你又做假定了吗?     有

37、时在编程序时,有必要对程序的运行环境做出某些假定。但这并不是说在编程序时,总要对运行环境作出假定。例如:下面的函数memset就没有对其运行环境做出任何假定。因此它虽然未必效率很高,但却能够运行在任何的ansi c 编译程序之下。也就是说,编译程序时要考虑移植,有许多是和编译器有关的是独立于ansi c 之外的特定编译器才能够运行正确的程序,这就是自己所做得假定,这种假定编程者心里一定要清楚才行:/* memset-用“byte”的值填充内存块 */void  *memset(  void

38、60; *pv, byte b, size_t  size)    byte  *pb = (byte *)pv;    while(size- > 0)        *pb+ = b;    return( pv );

39、推荐精选推荐精选    但在许多计算机上,先通过将要填充到内存块中的小值拼成较大的数据类型,然后用较大的大值填充内存,由于实际填充的次数减少了,可使编出的memset函数速度更快,在68000上,下面的memset函数的填充速度比上面的要快四倍。/* longfill-用“long”的值填充内存块。在填完了最后一个长字之后, * 返回一个指向所填第一个长字的指针 */long *longfill(long *pl, long l, size_t size)

40、;  /* 原型 */void *memset(void *pv, byte b, size_t size)    byte  *pb = (byte *)pv;    if(size >= sizethreshold)          &#

41、160;unsigned long l;       l = ( b<< 8) | b;       l = ( l << 16 ) | l;       pb = (byte 

42、;*)longfill( (long *)pb, l, size/4 );       size=size%4;      while(size- > 0)        *pb+ = b;    return( pv ); 

43、;   在上面的程序中,可能除了对sizethreshold所进行的测试之外,其他的内容都很直观。如果还不大明白为什么要进行这一测试,那么可以想一想无论是将4个字节拼成一个long,还是调用函数都化一定的时间。对sizethreshold进行测试是为了使memset只有在用long进行填充,使速度更快时才进行相应的填充。否则,就仍使用byte进行填充。    这个memeset新版本的唯一问题是他对编译程序和操作系统都作了一些假定。这段代码很明显的假定long占用4个内存字节,该字节的宽度是8位。这些假定对许多计算机都正确。

44、不过这并不意味着因此就应该对这一问题置之不理,因为现在正确并不等于今后也正确。    有的程序员“改进”这一程序的方法,是把它写成如下可移植性更好的形式:void *memset(void *pv, byte b, size_t size)    byte  *pb = (byte *)pv;    if(size >= sizethresh

45、old)           unsigned long l;       size_t sizesize;       l = 0;       for( sizesize = sizeof(lon

46、g); sizesize- > 0; null)           l = (l << char_bit) | b;       pb = (byte *)longfill( (long *)pb, l, size/sizeof(

47、long) );       size=size%sizeof(long);       while(size- > 0)        *pb+ = b;    return( pv );    由于在程序中大量的使用了运

48、算符sizeof,这个程序看起来移植性更好,但“看起来”不等于“就是”。如果要把它移植到新的环境,还是要对其进行考察才行。如果在macintosh plus或者其他基于68000的计算机上运行这个程序,假如pv开始指向的是奇数地址,该程序就会瘫痪。这是因为在68000上,byte * 和 long * 是不可以相互转换的类型,所以如果在奇数地址上存储long 就会引起硬件错误。    那么到底应该怎么做呢?    其实在这种情况下,就不应该企图将

49、memset写成一个可移植的函数。要接受不可移植这一事实,不要对其改动。对于68000,要避免上述的奇数地址问题,可以先用byte进行填充,填到偶数地址之后,再换用long继续填充。虽然将long对齐在偶数上已经可以工作了,但在各种基于68020、68030、68040等新型的machintosh上,如果使其对齐在4字节的边界上,性能会更好。至于对程序中所作的其他假定,可以利用断言和条件编译进行相应的验证:void *memset(void *pv, byte b, size_t size)   

50、0;byte  *pb = (byte *)pv;    #ifdef mc680x0    if(size >= sizethreshold)           unsigned long l;       assert(

51、0;sizeof(long) = 4 && char_bit = 8);       assert( sizethreshold >= 3);       /* 用字节进行填充,直到对齐在长字边界上 */       while( unsigned

52、 long) pb & 3) != 0)                  *pb+ = b;           size-;        

53、;      /* 现在拼装长字,并用长字填充其他的内存单元 */       l = ( b<< 8) | b;       l = ( l << 16 ) | l;    

54、   pb = (byte *)longfill( (long *)pb, l, size/sizeof(long) );       size=size%sizeof(long);       #endif  /* mc680x0 */   while(size- &

55、gt; 0)        *pb+ = b;    return( pv );    正如所见,程序中与具体及其相关的部分已经被mc680x0预处理程序定义设施括起。这样不仅可以避免这部分不可移植的代码被不小心地用到其他不同的机器上,而且通过在程序中搜索m680x0这个字符串,可以找出所有与目标机器有关的代码。    为了验证long占用个内存字

56、节、byte的宽度是8,还在程序中加了一个相当直观的断言。虽然暂时不太可能发生改变,但谁知道以后会不会发生改变呢?    为了在调用longfill之前使pv指向4字节的边界上,程序中使用了一个循环。由于不管size的值如何,这个循环最终都会执行到size等于3的倍数,所以在循环之前还加了一个检查sizethreshold是否至少是3的断言(sizethreshold应该取较大的值。但他至少应该是3,否则程序就不会工作)。    经过这些改动,很明显这个程序已不再可移植,原先所作的假定或者已经被消除,或者通过断言进

57、行了验证。这些措施使得程序极少可能被不正确地使用。   消除所作的隐式假定,或者利用断言检查其正确性 光承认编译程序还不够    在微软的历史上,曾经有过这么一件事情。他们的一些小组渐渐发现他们不得不对其代码进行重新的考察和整理,因为相当多的代码充满了“2”,而不是“sizeof(int)”、与上了0xffff,而不是unit_max进行无符号数的比较、在数据结构中使用的是int,而不是真正想用的16位数据类型这一类问题。    也许有人认为这是因为这些程序员太懒惰,但他们不会同意这一看法。事实上,他们认

58、为有很好的理由说明他们可以安全地使用“2”这种形式,及相应的c编译程序是由microsoft自己编写的。这一点给程序员造成了安全的家假象,正如几年前一位程序员所说:“编译程序组从来没有做使我们所有程序垮掉的改变”。    但这位程序员错了。    为了在intel 80386和更新的处理器上生成更快更小的程序,编译程序组改变了int的大小(以及其他一些方面)。虽然编译程序组并不想使公司内部的代码垮掉,但是保持在市场上的竞争地位显然更重要。毕竟,这是那些自己做了错误假定的microsoft程序员的过错。 推

59、荐精选    所以,大家应该考虑,自己的程序是否可以在以后发展了的64位机器上运行呢?是否可以在当前的其他系统上运行呢?是否具有移植性呢?是否具有代码可重用性呢?如果没有,那么你现在所编的程序的价值就非常的小,只是在学习而已。   不可能的事情也能发生?    函数的形参并不一定总是给出所有输入数据,有时它给出的只是一个指向函数输入数据的指针。例如:下面这个简单的压缩还原程序: byte *pbexpand(byte *pbfrom, byte *pbto,

60、0;size_t sizefrom)    byte b, *pbend;    size_t size;    pbend = pbfrom + sizefrom;  /*正好指向缓冲区尾的下一个位置*/    while( pbfrom < pbend)    

61、;        b = *pbfrom+;        if( b = brepeatcode)                    /*在pbto开始的位置存储“size”个“b”*

62、/            b = *pbfrom+;            size = (size_t) *pbfrom+;            while( size-

63、60;> 0)                *pbto+ = b;                else           &#

64、160; *pcto+ = b;        /*end if*/    /*end while*/    return(pbto);/*end pbexpand*/     本程序将一个数据缓冲区中的内容拷贝到另一个数据缓冲区中。但在拷贝过程中,它要找出所有的压缩字符序列。如果在输入数据中找到了特殊的字节brepeatcode,它就认

65、为其后的下两个字节分别是要重复的还原字符以及字符的重复次数。尽管这一过程显得有点过于简单,但我们还是可以把它们用在某些类似于程序编辑的场合下。那里,正文中常常包括有许多表示缩进的连续水平制表符和空格符。    为了使pbexpand更健壮,可以在该程序的入口点加上一个断言,来对pbfrom、sizefrom和pbto的有效性进行检查。实际上,还有许多其他可以做的事情。例如:可以对缓冲区中的数据进行确认。    由于进行一次译码总需要三个字节,所以相应的压缩程序从不对两个连续的字符进行压缩。另外,虽然也可以对三个连续

66、的字符进行压缩,但这样做并不能得到什么便宜。因此,压缩程序只对三个以上的连续字符进行压缩。    存在一个例外的情况。如果原始数据中有brepeatcode,就必须对其进行特殊的处理。否则当使用pbexpand时,就会把它误认为是一个压缩字符序列的开始。当压缩程序在原始数据中发现了brepeatcode时,就把它再重复一次,以便和真正的压缩字符序列区别。    总之,对于每个字符压缩序列,其重复次数至少是4,或者是1。在后一种情况下,相应的重复字符一定是brepeatcode本身。我们可以使用断言对这一点进行验证:

67、 推荐精选byte *pbexpand(byte *pbfrom, byte *pbto, size_t sizefrom)    byte b, *pbend;    size_t size;    assert(pbfrom != null  &&  pbto != null &

68、#160;&&  sizefrom != 0);    pbend = pbfrom + sizefrom;  /*正好指向缓冲区尾的下一个位置*/    while( pbfrom < pbend)            b =

69、0;*pbfrom+;        if( b = brepeatcode)                    /*在pbto开始的位置存储“size”个“b”*/          

70、  b = *pbfrom+;            size = (size_t) *pbfrom+;            assert(size >= 4  |  (size = 1 &#

71、160;&&  b = brepeatcode);            while( size- > 0)                *pbto+ = b;    

72、;            else             *pcto+ = b;        /*end if*/    /*end while*/   &

73、#160;return(pbto);/*end pbexpand*/     如果这一断言失败,说明pbfrom指向的内容不对或者字符压缩程序中有错误。无论那哪种情况都是错误,而且是不用断言就很难发现的错误 利用断言来检查不可能发生的情况安静的处理       程序员,尤其是有经验的程序员编的程序通常都是这样:当某些意料不到的事情发生时,程序只进行无声无息的安静处理,甚至有些程序员会有意识的使程序这样做。也许你自己用的是另一种方法。     &#

74、160; 当然,我们现在谈的是所谓的防错性程序设计。 推荐精选      前面,我们介绍了pbexpand程序。该函数使用的就是防错程序设计。但从其循环条件可以看出,下面的修改版本并没有使用防错性程序设计。 byte *pbexpand(byte *pbfrom, byte *pbto, size_t sizefrom)    byte b, *pbend;    s

75、ize_t size;    pbend = pbfrom + sizefrom;/*正好指向缓冲区尾的下一个位置*/    while(pbfrom != pbend)            b = *pbfrom+;       

76、60;if(b = brepeatcode)                    /* 在pbto开始的位置存储“size”个“b”*/            b = *pbfrom+;   

77、60;        size = (size_t) *pbfrom+;            do                *pbto += b;   

78、         while(size - != 0)                elae            *pbto + = b;    /* while *

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论