编写优质无错C程序秘诀 《经验谈》

发布 2019-08-27 09:13:37 阅读 6004

首先,当发现错误时,要不断就以下两个问题追问自己的结果:

1、怎样才能自动地查出这个错误?

2、怎样才能避免这个错误?

关于错误:错误可以分为两类:

1、开发某一功能时产生的错误。

2、程序员认为该功能已经开发完成之后仍然遗留在**中的错误。

第一种错误好解决,可以把编译器可以设置的警告等级开关打开,以及语法检查来排除;逻辑错误也可以使用跟踪手段来排除。跟踪逻辑错误就相对麻烦一些,要消除这些麻烦就要养成一个好的编程习惯和方法。

第二种错误时非常隐蔽的,需要长期的实践和经验在其中,还要对c语言具有深刻的了解才能够提高上来,这里就是要告诉大家一些这样的事情,通过**解说来阐明具体事实。

以下的文章里,实际上有许多是微软microsoft的老程序员开发word和excel的经验之谈,这也是我当初学习他们的经验时的体会和材料的总结和整理。

总之,这些对于在c道路上前进的人们是非常重要的,不敢独占,先拿出来以供大家享受。

第一个问题)

考虑自己所用的语言和编程环境?使空语句明显化!

充分利用语言的特性和编程环境,把所有环境下的调试报错等级开关都打开,注意使用语言的保留字,例如下面的两段程序对比:

*复制一个不重叠的内存块*/

void*memcpy(void*pvto,void*pvfrom,size_tsize)

byte*pbto=(byte*)pvto;

byte*pbfrom=(byte*)pvfrom;

while(size-->0);

pbto++=pbfrom++;

return(pvto);

从以上缩进格式可以看出,while后的分号肯定是一个错误。但编译器认为这是一个合法的语句,允许循环体为空语句。报警开关都打开时,大多编译器都能够报出这一错误。

但需要用空语句时,最好实用null(大写)明确出来:

char*strcpy(char*pchto,char*pchfrom)

char*pchstart=pchto;

while(*pchto++=pchfrom++)

null;/*此处null大写*/

return(pchstart);

这样,编译器编译程序接受显式的null语句,把隐式空语句自动地当做错误标出。

第二个问题)

无意的赋值。

例如:if(ch='

expandtab();

有些编译器允许在程序&&和||表达式以及if、for和while中直接使用赋值的地方禁止简单赋值,如果以上五种情况将==偶然地键入为=号,就会报错。

while(*pchto++=pchfrom++)

null;编译程序就会产生警告信息,为了防止这种情况出现,可以这样做:

while((*pchto++=pchfrom++)

null;这样做的结果由两个好处:

1、现在的编译器不会为这种冗余的比较产生额外的**和开销,可以将其优化掉。

2、可以少冒风险,尽管以上两种都合法,但这是更安全的用法。

第三个问题)

参数错误:例如:

fprintf(stderr,"unabletoopenfile%s. "filename);

fputc(stderr,'

这个程序看上去好像没有问题,实际上fputc的参数顺序错了。幸好ansic提供了函数原型,在编译时自动查出这些错误。

ansic标准要求每个库函数都必须有原型,中可以查到:

intfputc(intc,file*stream);

如果在程序文件头里给出了原型,这类错误就可以检查出。

ansic虽然要求标准库函数必须有原型,但并不要求用户编写的函数也必须有原型。可以有,也可以没有。有些程序员经常抱怨对函数的原型进行维护,如果没有原型,就不得不依靠传统的测试方法来查出程序中的调用错误,大家可以扪心自问:

究竟哪个更重要?

利用原型可以生成质量更好的**。ansic标准使得编译程序可以根据原型信息进行相应的优化。

有这样的名言:

投资者与赌徒之间的区别在于投资者利用每一次机会,无论它是多么小,去争取利益;而赌徒则只靠运气。我们应该将这一概念同样应用于编程活动。

把所有的警告开关都打开,除非有极好的理由才不这样做!

原则一)如果有单元测试,就进行单元测试。

你认识那个程序员宁愿花费时间去跟踪排错,而不是编写新的**?肯定有这样的程序员,但我至今还没有见到一个。

当你写程序时,要在心中时刻牢记着假想编译程序这一概念,这样就可以毫不费力或者直费很少力气利用每个机会抓住错误。

如果想要快速容易地发现错误,就要利用工具的相应特性对错误进行定位。错误定位的越早,就能够越早地投身于更有趣的工作。

努力减少程序员查错的技巧。可以选择编译程序的环境来实现。高级的编码方法虽然可以查出或减少错误,但它们也要求程序要有较多的技巧,因为程序员必须学习这些高级的编码方法。

原则二)自己设计并使用断言。

利用编译器自动查错固然好,但实际上只是很少一部分。如果排除掉了程序中的所有错误,大部分时间程序会正确工作。

看一下下列**:

strcopy=memcpy(malloc(length),str,length);

该语句在多数情况下会工作的很好,除非malloc的调用产生失败。一旦产生,就会给memcpy返回一个null指针,而memcpy处理不了null指针,这样的错误产生,如果在交付用户之前将导致程序的瘫痪。但如果交付了用户,那用户就一定“走运”了。

解决方法:对null指针进行检查,如果为null,就给出一条错误信息,并终止memcpy执行。ee

*拷贝不重叠的内存块*/

voidmemcpy(void*pvto,void*pvfrom,size_tsize)

void*pbto=(byte*)pvto;

void*pbfrom=(byte*)pvfrom;

if(pvto==null||pvfrom==null)

fprintf(stderr,"badargsinmemcpy! "

abort();

while(size-->0)

pbto++=pbfrom++;

return(pvto);

只要调用时错用了null指针,这个函数就会查出来。但测试的**增加了一倍,降低了执行速度,这样“越治病越糟”,还有没有更好的方法?

有,利用c的预处理程序!

这样就会保存两个版本。一个整洁快速,用于交付用户;另一个臃肿缓慢(包含了额外的检查),用于调试。这样就要同时维护同一个程序的两个版本,利用c的预处理程序有条件地包含相应的部分。

例如:只有定义了debug时,才对应null指针测试。

voidmemcpy(void*pvto,void*pvfrom,size_tsize)

void*pbto=(byte*)pvto;

void*pbfrom=(byte*)pvfrom;

#ifdefdebug

if(pvto==null||pvfrom==null)

fprintf(stderr,"badargsinmemcpy! "

abort();

#endif

while(size-->0)

pbto++=pbfrom++;

return(pvto);

这样,调试编译时开放debug,进行测试程序和找错;交付用户时,关闭debug后进行编译,封装之后交给经销商。

这种方法的关键是保证调试**不在最终产品**现。

那么还有没有比以上两种更好的方法,有!下次再讲。

准则二续)利用断言进行补救。

实际上,memcpy中的调试**编的非常蹩脚,喧宾夺主。他能产生好的效果,这无疑,但许多程序员不会让他这样存在的,聪明的程序员会让调试**隐藏在断言assert中。

assert是个宏,定义在头文件中,每个编译器都自带。前面的程序完全可以使用assert来处理,看一下下面**,把7行减为了1行**。

voidmemcpy(void*pvto,void*pvfrom,size_tsize)

void*pbto=(byte*)pvto;

void*pbfrom=(byte*)pvfrom;

assert(pvto!=null&&pvfrom!=null);

while(size-->0)

pbto++=pbfrom++;

return(pvto);

这里要强调的是:assert是个只有定义了debug才起作用的宏,如果其参数的计算结果为假,就中止调用程序的执行。

当然程序编制也可以编制自己的断言宏,但要注意不要和assert冲突,因为assert是全局的。举个例子:

先定义宏assert:

#ifdefdebug

void_assert(char*,unsigned);/自定义断言函数的函数原型*/

#defineassert(f)

if(f)null;

esleassert(_file_,_line_);

#else#defineassert(f)null

#endif

从上述我们可以看到,如果定义了debug,assert将扩展为一个if语句。

当assert失败时,他就是用预处理程序根据_file_和_line_所提供的文件名和行号参数调用_assert。_assert在标准错误输出设备stderr上打印一条错误信息,然后中止:

void_assert(char*strfile,unsigneduline)

fflush(stdout);

fprintf(stderr," assertionfailed:%s,line%u ",strfile,uline);

fflush(stderr);

abort();

职业健康安全管理手册编写工作程序

行业资料 单位。部门。日期 年 月 日。职业健康安全管理手册是在职业健康安全管理体系的设计阶段形成的文件,该文件应当按照ohsms体系分析的结果,对体系的构成 各要素的内容及其相互之间的联系作出系统 明确和原则的规定,在初始评审和体系设计之后编制。编制职业健康安全管理手册工作程序。1 资料收集与分析...

职业健康安全管理手册编写工作程序

职业健康安全管理手册是在职业健康安全管理体系的设计阶段形成的文件,该文件应当按照ohsms体系分析的结果,对体系的构成 各要素的内容及其相互之间的联系作出系统 明确和原则的规定,在初始评审和体系设计之后编制。1 资料收集与分析。需要收集的主要资料包括 a 用人单位机构现状 b 各部门职责和权限现状 ...

C语言程序设计大赛通知

在深化高等教育改革 全面推进素质教育的新形势下,为进一步丰富在校学生程序设计竞赛的形式和内容,同时提高广大学生的学习积极性 创新意识和勇于实践的科学精神。经研究决定于举办我学院第四届c语言程序设计大赛。主办单位 河南城建学院计算机科学与工程学院。为保证此次赛事在公平 公正的环境下顺利进行,大赛特设立...