做企业网站服务器,电子软件开发工资多少钱,温州网吧什么时候恢复营业,wordpress默认图像不显示无论一门语言有多么流行或多么优秀#xff0c;它总是存在一些问题#xff0c;#xff23;语言也不例外。本章讨论的重点是#xff23;语言本身存在的问题#xff0c;作者煞费苦心的用一个太空任务和软件的故事开头#xff0c;也用另一个太空任务和软件的故事结尾#xf… 无论一门语言有多么流行或多么优秀它总是存在一些问题语言也不例外。本章讨论的重点是语言本身存在的问题作者煞费苦心的用一个太空任务和软件的故事开头也用另一个太空任务和软件的故事结尾引人入胜。 关于这两个故事在这里不说有兴趣的朋友还是建议买这本书去看看这本书用相当轻松的文字而又不失深沉地向我们道来语言的各种特性与特别的用法。 书中提到一种分析编程语言缺陷的方法让我们能够详细的去分析各种编程语言的缺陷即把所有的缺陷归于类不该做的做了(多做之过)、该做的没做(少做之过)、该做的做了但不合适(误做之过)本章也是按照这样一种分析方法来分析语言本身存在的一些问题由于是一门神奇的语言被许多平台所选用也被大家所学习所以了解语言是一件相当有必要的事情本章就是从缺陷来了解语言。 多做之过就是语言中存在某些不应该存在的特性包括容易出错的switch语句、相邻字符串常量自动连接和缺省全局作用域。 首先说说switch语句吧这个语句在多条件的时候使用率还是相当高的相比大量if语句我还是比较倾向于它的。switch语句的一般形式如下 switch(表达式) { case 常量表达式1:语句1; break; .... case 常量表达式n:语句n; break; default:语句;break; } 每个case结构由个部分组成关键字case;其后的常量表达式;以及后面的冒号当表达式的值与case后面的常量表达式匹配时case后面的语句就会执行否则执行default后面的语句default都可以出现在case列表出现的任何位置如果没有default语句那么switch语句就什么也不做你不要指望它会提醒你它什么都没做。在语言中几乎从来不进行运行时错误检查——对进行解引用操作的指针进行有效性检查大概是唯一的例外这是因为运行时检查与语言的设计理念相违背按照语言的理念程序员应该知道自己在干什么而且保证自己的所作所为是正确的。switch的另一个问题是它内部的任何语句都可以加上标签并在执行时跳转到那里作者给出了一个例子那就是当你的default语句写错的时候比如把字母写成了数字看起来很像对吧defau1t不过功能可是大不相同这意味着如果表达式不匹配任何常量表达式时它将什么也不干因为没有default语句啊然而即使这样编译器也无法检查出错误来。当然switch语句里最大的问题还不是这个而是它不会在每个case语句执行完毕后自动跳出如果你不使用break语句来跳出它将一直执行下去在《与指针》描述switch语句时有一句话我觉得非常合适那就是case语句只是确认进入switch语句的入口如果你不使用break语句那么出口都是在swtich语句的右花括号那里作者还举了一个利用switch语句这种特性的例子用来计算程序输入中字符、单词和行的个数有趣的是这个例子正是需要switch语句一直执行下去而不是遇到一个case就退出有兴趣的朋友可以参考《与指针》第四章的内容。当然如果在这种情况下要使用switch语句的特性那么一句注释FALL THRU是必不可少的它会告诉你我就是要利用这个特性我不需要break语句不过在绝大多数情况下是不会需要这种特性的。以上就是switch语句存在的三个主要问题。 其次相邻字符串常量的自动合并这个约定也会带来一些问题。在printf的使用中这是一个优点因为你不用担心要输出的字符串有多长你可以放心的用双引号包括每一行的内容反正它会自动合并比方说 printf(A second favorite childrens book is Thoms the tank engine and the Naughty Enginedriver who tied down thomass boiler safety value); 这个printf语句会自动连接三个行这可以使每一行的代码看起来简洁而又完整不过你该担心的是下列情况 char *available_resouces[] { color monitor, big disk Cray /*少了一个逗号*/ on-line drawing routhines, ... 在这种情况下我们都知道由于数组大小的缺省而少了逗号会使两个字符串常量自动连接所以在编译器看来这并不是一个错误它也就不会提示你而程序可能会莫名其妙的运行打印Crayon-line drawing routhines“或是修改其他变量因为字符串数目比预期少了一个。 缺省可见性这个问题主要体现在全局函数的定义上我们知道在声明函数的时候如果没有任何关键字限制那么会被自动定义为全局函数除非你加上static关键字才能限制对这个函数的访问。事实上几乎所有人都没有在函数名前添加存储类型说明符的习惯所以绝大多数函数都是全局可见的然而根据实际经验这个缺省的全局可见性多次被证明是个错误。软件对象在大多数情况下应该缺省的采用有限可见性当程序员需要让它全局可见时应该采用显式的手段原因在于这种大范围的全局可见性会与C语言的另一个特性产生影响也就是用户编写和库函数同名的函数并取而代之的行为。这也说明了在C语言中对信息可见性的选择很有限要么是extern意味着整个库的所有对象都可见要么是static对其他文件都不可见。 所谓”误作之过“就是语言中有误导性质或是不适当的特性这些特性跟C语言的简洁性有关有些则与操作符的优先级有关。 C语言存在的其中一个问题就是它太简洁了仅增加、修改或删除一个字符就会使原先的程序变成另外一个仍然有效但全然不同的程序这就意味着如果你在一个小问题上出了一点问题那么编译器是不会检查出来提示你的因为你的程序仍然有效。当然还造成一个问题那就是很多符号同时具有好几种意思你要直到它到底是什么意思还要根据上下文来这一点尤其体现在作用域上。比方说static关键字就曾经令我疑惑它有时候表示静态变量有时候又表示内部链接属性那么它到底代表什么呢正确的答案是这样的在函数内部表示静态变量当表示函数时代表内部链接属性。同样的extern关键字也是这样在缺省可见性已经提到extern的外部链接属性不应该作为缺省属性。还有操作符既表示取地址操作符又表示按位与操作同样*操作符也有多种含义最明显的、用法最多的操作符可能还是要数()操作符了它们无处不在。一个符号所表达的意思越多编译器就越难检测到这个符号在你的使用中所存在的异常情况。 另外在操作符的优先级上我完全能够感同身受初学C语言甚至在学完C语言很久一段时间之内我都没有真正的完全搞清楚过操作符的优先级凭感觉用吧一般来说结果都是错的不过用多了可能也就会了。还记得-这个操作符在结构指针中的使用吗我们知道-这个操作符是对一个结构成员进行解引用它所代表的意思p-f也就相当于(*p).f不过千万别忘了添加括号哦因为”.操作符的优先级大于*这个问题也是导致-操作符出现的原因之一类似的还有很多比如[]的优先级高于*int *p[]这个表达式呢代表p是一个元素为int指针的数组而不是说p是个指向int数组的指针哦。不过在多年前Dennis Ritchie解释了这些不正常的情况是如何由于历史的偶然原因而产生的最大的原因还是如果现在把它们更改过来的话现有的大量代码都可能出现问题。 最后少做之过的特性就是语言应该提供但未提供的特性如标准参数处理以及把lint程序错误的从编译器中分离出来。 标准参数处理这个问题不管是在UNIX还是在语言中都没有得到好好的处理因为参数与文件名程序是分不清楚的。其中一个例子就是在在UNIX中创建一个文件文件名以’-‘连字符开头然后却发现无法用rm命令把连字符去掉这就是它分不清文件名与参数的影响书中还给出一个有趣的实例——关于在1990年以前给“用户名的第二个字母是f的用户”发邮件那么他将收不到进一步让我们理解分不清参数与文件名的影响。 而lint程序甚至现在好多使用C语言的人都没有听过在早期的C语言中语言设计者作出了明确的规定——把编译器中所有的语义检查措施全部分离出来错误检查由一个单独的程序完成这个程序被称为“lint”在省掉lint之后编译器可以做得更小更快而且更简单所以理所当然的它被去掉了不过所付出的代价是代码中悄悄混入了大量的Bug和不可靠的编码风格许多程序员缺省情况下在每次编译中并不使用lint。在书中给出了一些实例是一些程序员在写代码的过程中容易犯得错误而编译器又检查不出如果使用lint程序则可以全部检查出来所以作者大力推荐使用lint程序作为检查。 下面来介绍一下这个lint程序吧。 lint程序不但可以检查出可移植性问题而且可以检查出那些虽然可移植并且完全合乎语法但却很可能是错误的特性lint程序会产生一系列程序员有必要从头到尾仔细阅读的诊断信息。 这是lint程序的系统版本 UNIX系统 在UNIX系统中可自动获得lint它是一个标准的UNIX工具。 Linux系统 在Linux各种发行版中,使用lint的版本是GNU下的Splint(前身是LClint) Windows 在Windows系统中从第三方获得的lint工具的名称是PC lint以及Splint 在这里由于我使用的是Linux所以介绍一下Linux中lint的使用。 首先安装splint工具 sudo apt install splint 然后假定你要检查的文件是main.c splint main.c 其中main.c中代码如下所示使用了switch语句来测试 #include stdio.hint main(void)
{int x;scanf(%d,x);switch(x){case 3:printf(4\n);case 4:printf(4\n);}return 0;
} 如果是直接gcc main.c那么不会有任何提示使用splint程序之后它显示了这些文本 Splint 3.1.2 --- 03 May 2009main.c: (in function main)
main.c:6:5: Return value (type int) ignored: scanf(%d, x)Result returned by function call is not used. If this is intended, can castresult to (void) to eliminate message. (Use -retvalint to inhibit warning)
main.c:10:10: Fall through case (no preceding break)Execution falls through from the previous case (use /*fallthrough*/ to markfallthrough cases). (Use -casebreak to inhibit warning)Finished checking --- 2 code warnings 显而易见的是它给出了两条提示一条是说你的scanf语句的返回值并没有用另一条就是switch语句没有break语句并且提示你如果确实不需要break语句请用/*fallthrough*/把它注释出来。 所以多用lint程序来检查你的程序吧说不定会给你一个惊喜。 转载于:https://www.cnblogs.com/monster-prince/p/6207683.html