mysql 网站 数据库,郑州网站建设中国建设建设银行,如何设置企业网站,备案网站公共查询系统C语言是被使用的最广泛的一种高级语言#xff0c;其历史相当久远。而其发展也相当神速#xff0c; 从当初的标准C发展到后来的C。其性能也发生了很多很大的变化。C语言拥有众多的编译器#xff0c;其中不乏优秀者众多。从当初的Turbo C引入集成化编译环境后#xff0c;C语言…C语言是被使用的最广泛的一种高级语言其历史相当久远。而其发展也相当神速 从当初的标准C发展到后来的C。其性能也发生了很多很大的变化。C语言拥有众多的编译器其中不乏优秀者众多。从当初的Turbo C引入集成化编译环境后C语言就以其灵活性高效率可移植性好深入人心。后来发展起来的C,Java 等语言无不是在 其基础进行扩充使其更为灵活更方便易用。新的C编译器引入了很多特色。使得C语言语法更加灵活。摒弃了标准C对语法死板的要求。使得编程随心所欲。这里推荐 Borland C 和Visual C.当然这是指Dos应用如果开发Windows应用程序那么当 首推Visual C.Visual C的可视化及自动代码生成功能相当强大。尤其它提供的 Wizard 和Appstudio使得开发程序简直成为一种享受。而且由于Visual C在各版本之间的连续性使得开发者不必经历换版带来的痛苦。从其1.5版直到最新的5.0版兼容性保持的很好。而且在VC中也包含了控制台应用Dos以及Windows Application,application wizard各种应用所以是一个强大的开发包。 学习C语言起初会觉得要记的东西太多这是由于它太灵活了。但是学到一定程度就会尝到甜头了。这种灵活性带来的是可读性好语法简单效率高。当然C语言最大的 特色还是它的指针对指针的透彻理解将是今后开发工作中的得力助手。因为在C中指针无处不见很多参数就完全是指针化的。虽然Java中摒弃了指针那是从安全性 方面考虑。如果从性能上来说那是大亏了。所以指针是一个核心。要学好C语言无非要透彻理解书本概念辅之以大量上机编程。要想提高应用水平 就要多看些应用方面的书。比如看看数据结构然后自己想办法来实现其中的算法。总之编程是靠编出来的不是靠看出来的。在调试程序时尽量自己解决实在 解决不了可以请教老师总之独立思考很重要。有条件的话在网上提问题可受到事半功倍的效果。坚持下去相信不久你就会成功的喜悦了。 以上所述旨在抛砖引玉若有不当敬请见谅 编写优质无错代码 一. 引言八月上旬深圳举办了一个讨论会主题是编写优质无错代码。这个讨论会吸引了深圳各大软件公司通信公司的程序员系统分析员参加并在讨论会后纷纷表示这种讨论会很有实际价值希望将这种形式的讨论会继续下去形成一个论坛以提高大家的编程水平和交换有价值的信息资料。 这个活动的发起是从网络上开始的。我偶然看到了这个讨论会的论题发生了兴趣。本来周末的我一般是很懒的没什么事情是不会出门的。而当我看到这论题后就给举办者发信表示愿意参加。于是一个周六的下午我就坐在了讨论会的现场。参加完这个讨论后我觉得有必要把其中的精华部分写下来和网络上的广大程序员共享于是就有了这篇文章。 二.主题编写优质无错的代码---讨论会主题。相信每个程序员都有这种希望谁都不愿意自己写出来的代码在release之后出错需要不停的修改维护。但是主持人提出了这样一个问题编写优质无错代码是否必要 为什么呢我稍微解释一下。在项目的时间很紧张的时候是按期完成任务重要还是代码的稳定性优质无错重要呢 主持人提出的四个具体问题是1、编写优质无错的代码的代价是什么2、代码的质量重要还是编写效率重要 3、在压力的情况下你会牺牲质量来提高效率么4、编写优质无错的代码是否意味着效率的降低?对于第一个问题编写优质无错代码的代价当然是时间不过随着编程人员的经验逐渐丰富所需要的时间也逐渐减少。对于第二个问题代码的质量比编写效率重要。当你花了1周时间写出来的代码需要你花一个月或者更长的时间去debug, 去修改错误这种效率的损失是得不偿失的。对于第三个问题 这需要看项目经理或者产品经理的态度和专业精神了。如果在一个专业的项目经理或者产品经理的指挥下当然是首先保证质量其次提高效率。而对于某些项目经理或者产品经理来说按时完成任务是最重要的他们往往不在乎在软件发布之后花比开发时间长得多的时间去修改程序维护错误。因为对于他们来说首先是要完成任务好给上级领导交差至于后期维护就是另外一个任务了维护花的时间多正说明了他这个项目的复杂性和难度。而对于开发人员来讲所希望的则正好相反。开发人员不喜欢花太多的时间在一个烂摊子上。所以在讨论会上大家纷纷表示应该让项目经理或者产品经理也来听一听这个讨论会:-)。对于第四个问题当然优质无错代码不是意味着效率的降低而是正好相反对提高效率有很好的促进作用。一个版本发布之后如果因为错误太多开发人员不得不去花很多时间修改bug, 甚至要从系统的体系结构方面去做大的改动重新编写部分代码这种效率的降低才是更大更不能承受的。而且花了太多的时间在老版本的维护上必然影响到新版本的工程进度直接影响到整个产品线的质量和进度严重的甚至会毁掉整个产品。 对于这一个主题我的回答是在时间允许的范围之内尽量提高代码的质量不追求慢工出细活不追求代码的100%无错但是要保证99%以上的无错。这样在时间的压力下在质量要求的束缚下就要求程序员有一个良好的习惯和稳健的编程风格以保证代码的优质无错。这就是第二个问题什么是编写优质无错的代码的核心思想优质无错是相对的而不是绝对的。任何代码都不可能说是绝对无错的但是在绝大部分情况下是稳定的强健的优质的无错的。每次发布的时候都会对上次的发布版本做若干修改增强功能的同时也要修改若干bug。那么核心思想就是:怎样才能自动地查出这个错误。怎样才能避免这个错误。 三.编写优质无错代码的经验在说了上面很多理论性的问题之后来看一看具体问题。先来看一看一个具体的题目我本人就是先在网上看了这个题目才对这个讨论会发生兴趣的 题目1:作为开发团队的一员你需要实现一些库函数提供给其他人使用。假设你实现的一个函数原型如下 int DoSomeThing(char* pParam){...} 你们约定好参数pParam不能为NULL但为了防止调用者错误传递NULL你需要在你的函数里做判断处理。请问你会选择那种方式并说明原因 (a) if (!pParam)return 0; (b) if (!pParam)return ERROR_PARAM; (c) if (!pParam)pParam ;... (d) if (!pParam)throw EXCEPTION_ERROR_PARAM; (e) if (!pParam)MessageBox(...); (f) assert(!pParam); 附加说明一点基于目前开发人员技术分布情况和参与讨论会的人员的技术分布情况这次所列举的例子都是C/C和Java方面的不涉及到VB, PB,Delphi等语言。不过对于这些程序员讨论也是有借鉴作用的。关于这个问题大概是所有的程序员都会遇到的。所以在网上和讨论会上都发生了激烈的争论和意见交换。我大概把主要的几种观点记录了一下列举在下面 1、选择f的理由因为非NULL是约定所以可以确定是调用者的问题f可以明确地指出这一点防止错误扩散。我的附加说明: 防止错误扩散的意思是如果用其他方式比如throwexception的方式这个异常不一定会在调用此函数的上一层被捕捉到可能会被继续抛出直到最上一层或者直到在某一层被catch到这样的话错误就会距离发生地点很远扩散开来。这一观点代表了一大部分的程序员的观点。 2、反对用f不赞成assert, assert更重要的作用是程序体里面的一个注释, 在阅读程序的时候起作用不能依赖他来检测错误, 很大程度上assert容易使使用者依赖它本不应该依赖的东西。这也代表了部分程序员的观点认为assert是不可依赖的而应该依赖于错误检测比如返回值或者异常。 3、另外一种观点f和d都可取。如果没有系统开销的考虑d则更好些。可以一举两得。如果没人catch这个exception其结果就跟f一样按bug处理dump core留下一stack trace。如果有人catch那就按运行错误处理......但是返回一特初值表示错误只是将错误上交掩耳盗铃而已。最终总得有个人assert,messagebox,throw exception,perrorexit,或别得什么的。既然已经是约定就干脆付起责任。 4、一种反对d的理由不可用d, 这就像你用人,却不相信人一样,偏要try,catch防范他。其实那个错是自己造成的,如果看到异常就容易不检讨自己。 5、关于观点3的支持意见讨论过程中有人认为assert检查的是bug, 而异常是可以恢复的意外情况。所以观点3的支持者说可恢复的意外是可以理解的但可恢复的bug就没什么意义了。既然已经约定好了你再违背就属于是bug而不是意外了(比如打不开文件什么的)。很多库函数都不检查指针的合法性(除了系统调用以外因为总不能让系统dump core吧)也不检查指针是否为NULL(因为如果层层都检查必定劳民伤财干脆让最上面调用的人在调用之外查)。 6、选择df选fd, 好处如下a以最激烈的方式充分暴露调用都的错误能及时修改BUGb便于调试,问题出现后直接到事故现场。比120还快c对于realse版的代码没有任何副作用。d以处理的代价来看 采用断言也是编写最小一种。e它是多语种多平台所通用的方式 如:C /C VB,Java1.4 在win ,unix通吃, 便于移置!如果在现实中测试没有能找到所有的BUG,那可能就要用异常来帮忙了 当然我也提出了我的观点, 我支持观点6。理由如下assert只在debug标志的时候有用而在编译release版本的时候不起作用。assert对于检查硬编码的错误是非常有用的能够及时的查处编码的错误。比如borland c的类库源代码中就有很多这样的assert。但是assert不是万能的因为有很多错误的发生不是完全在编译时发生的而是运行时的错误。在release后assert是不可能依赖的。那么我们就需要exception这一机制来检测运行时错误并相应的做出处理。当然在异常检测和处理过程中还有许多需要讨论的问题由于不是这一题目的范围我们没有必要继续讨论得太多但是提出来希望大家注意异常不是捕获了就完成任务了而要对于不同的情况采取不同的处理办法千万不能只是捕获而不做任何处理那样和不捕获异常没有任何别。 在题目刚刚提出的时候选择各种答案的人都有所以我有必要在这里把其他答案为什么不能选的理由说一下。 (a) if (!pParam)return 0;这是很多初级程序员常常采取的一种方式。返回值设为0。 因为函数的返回值往往是计算的结果不赞成把错误标志值和计算结果混在一起使用容易造成使用者的误会。当然在很多unix函数中由于历史原因还存在很多这样子的函数所以需要指出不要沿用这种方式。 (b) if (!pParam)return ERROR_PARAM;b比a稍微好一点点返回了一个常量或者预定义的宏。 从返回值的字面上调用者能知道发生了什么错误但是这也不是一种好的方法。 (c) if (!pParam)pParam ;...这是最不好的方式。直接给pParam赋予空字符串然后继续函数过程这容易造成不可预料的后果是程序不稳定的根源。 (d) if (!pParam)throw EXCEPTION_ERROR_PARAM;抛出异常刚刚已经讨论过了不再赘述。 (e) if (!pParam)MessageBox(...);这是一种比较可笑的方式当然也有不少人用。MessageBox是直接弹出一个对话框告诉使用者出错了。但是并不做任何处理程序继续往下执行直到出错崩溃。呵呵 (f) assert(!pParam);断言刚刚已经讨论过了不再赘述。 以上这个题目引发了所有与会者的兴趣讨论异常热烈最后主持人也给出了自己的观点:df。当然这并不是标准答案因为编程这一门课程本来就没有什么标准答案大家见仁见智这个答案只是经验的积累。 主持人紧接着列出了编写优质无错代码的经验:a.理想的编译器和实际的编译器b.使用断言c.函数的界面设计d.考虑风险e.态度的问题 以上是本节的主要内容。断言刚刚的问题中已经讨论过了来看看其他的内容。 理想的编译器和实际的编译器: 题目二下面memcpy函数实现有什么问题Void *memcpy(void *pvTo,void *pvFrom,size_t size){byte *pbTo(byte *) pvTo;byte *pbFrom(byte *)pvFromwhile(size -- 0)*pbTo *pbFrom;return pvTo;} 呵呵粗略一看这函数还真找不出问题来。但是仔细看看你就会发现while(size -- 0)这里多了一个分号导致下面的*pbTo *pbFrom;不是在while循环中执行多次而是只执行了一次。当然这不是函数设计者的预期结果而编译器却不会报告错误因为while(size -- 0)从语法上来讲并没有错误。这就是理想的编译器和实际的编译器的区别所在。 那么该怎么检查这种错误呢主持人提出了如下办法 while(size -- 0) NULL; 可以加入NULL来解决空语句. 这样子当你需要 while(size -- 0)*pbTo *pbFrom;这种形式的时候就不会发生错误了只需要用眼睛看看就能发现了。两点好处 1 无冗余代码2 使人更明白。减少风险. 还有人会这么写if( (nread(....)) 1) ....在这里赋值符号和判断相等的符号容易敲错而编译器又检查不出来可能就会有如下错误If(ch ‘ ’)...这也是需要注意的问题。 理想的编译器和实际的编译器小结a.把屡次出错的合法的C习惯用法看成程序中的错误 b.增强编译器的警告级别c.使用其它的工具来检查代码 如 Lint 等d.进行单元测试e.消除程序错误的最好方法是尽可能早、尽可能容易地发现错误要寻求费力最小的自动查错的方法f.努力减少程序员查错所需的技巧 使用断言题目三下面函数实现哪一个好为什么a.char Uptolower(char ch){if(ch ‘A’ ch ‘Z’)return ch‘a’-’A’;return -1;}b.char Uptolower(char ch){assert(ch ‘A’ ch ‘Z’)if(ch ‘A’ ch ‘Z’)return ch‘a’-’A’;return ch;}c.char Uptolower(char ch){assert(ch ‘A’ ch ‘Z’)return ch(‘a’-’A’);}分析a.该函数检查ch是否在A..Z之间如果是则返回相应的小写字符如果 不是则返回-1。缺点在于把错误标志值和计算结果混在一起使用容易造成使用者的误会。 b.该函数使用了断言如果ch在A..Z之间则返回相应的小写字符如果不是断言会起作用程序发生错误并退出。而最后一个return ch;则是在release的时候如果不是A..Z之间则返回原来的字符。但是从书写效率上来说这个函数稍微罗嗦了一点。因为它重复使用了断言和if判断。 c.该函数也使用了断言返回相应大写字母的小写字母。 使用断言的好处a.暴露了调用者的错误b.便于调试c.对代码没有代价d.最少的处理代价 断言使用举例void memcpy(void * pvTo,void *pvFrom,size_t size){void *pbTo (byte *)pvTo;void *pbFrom (byte * pvFrom);assert(pvTo !NULL pvFrom !NULL);assert(pbTo pbFrom size pbFrom pbTosize);…} 使用断言的规则a.要使用断言对函数参数进行确认b.要从程序中删去无定义的特性或者在程序中使 用断言来检查出无定义特性的非法使用 c.不要浪费别人的时间-详细说明不清楚的断言d.消除所做的隐式假定或者利用检查其正确性e.在进行防错性程序设计时不要隐瞒错误防错性程序设计虽然被誉为有较好的编码风格但它却隐瞒了错误。要记住我们正在谈论的错误决不应该再发生而对这些错所进行的安全处理又编写无错代码变得更加困难f.要利用不同的算法对程序的结果进行确认g.不要等待错误发生要使用初始检查程序 断言小结a.要同时维护交付和调试两个版本。封装交付的版本应尽可能地使用调试版本进行自动查错。b.断言是进行调试检查的简单方法。要使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别后者是在最终产品中必须处理的。 c.使用断言对函数的参数进行确认并且在程序员使用了无定义的特性时向程序员报警。涵数定义得越严格确认其参数就越容易。d.防错性程序设计会隐瞒错误。在进行防错编码时如果”不可能发生”的情况确实发生了要使用断言进行报警。 写到这里我们初步探讨了编写优质无错代码的必要性原则和相关经验。留几个练习题目大家也参与一下讨论吧。练习题目1:下面的memset函数实现有什么问题 void *memset(void *pv, byte b, size_t size){byte *pb (byte *)pv;unsigned long l;size_t sizeSize; l (b 8) | b; /* 用4个字节拼成一个long */l (l 16) | l;pb (byte *)longfill((long *)pb, l, size/4);size size % 4; while (size-- 0)*pb b;return (pv);} 练习题目2: 下面的代码用memset将三个局部变量置为0请问可能会有什么问题void DoSomeThing(...){int i;int j;int k; memset(k, 0, 3*sizeof(int)); // 将i,j,k置为0...} 练习题目3: 定义结构如下typedef struct{char c1;char c2;int n;} stru;请问sizeof(stru)等于多少并说明理由。 练习题目4: 下面是C语言中两种if语句判断方式。请问哪种写法更好为什么int n; if (n 10) // 第一种判断方式if (10 n) // 第二种判断方式 练习题目5: 下面的代码有什么问题void DoSomeThing(...){char* p;...p malloc(1024); // 分配1K的空间if (NULL p)return;...p realloc(p, 2048); // 空间不够重新分配到2Kif (NULL p)return;...} 练习题目6: 下面的代码有什么问题char *DoSomeThing(...){char str[16]; ...return str;} 练习题目7:下面的代码有什么问题 char *_strdup( const char *strSource ){static char str[MAX_STR_LEN]; strcpy(str, strSource);return str;} 练习题目8: 下面的代码有什么问题并请给出正确的写法。try{FILE* fp fopen(c:1.dat);if (NULL ! fp){...}fclose(fp);}except(EXCEPTION_EXECUTE_HANDLER){} 我敲字敲累了告一段落吧。不过讨论会并不止讨论了这些内容还有很多内容我没有写完比如函数的界面, 编写代码的风险, 编程的态度等等问题。作为补充我把讨论会的幻灯片修改成了文本版本作为另外一篇文章放在这里以便对这个话题感兴趣的网友们参考。 转载于:https://www.cnblogs.com/carekee/articles/2569784.html