我们在学校学习或者有参加过C语言培训的话,应该都听说过“不建议使用goto语句”。但是一般不会有人告诉你为什么不建议使用goto语句,类似于这种存在但不建议使用的关键词还有很多。
今天,我们就一起来看看那些在C语言中存在但是不建议使用的那些关键词!
二、慎用goto关键词关于goto语句的争议已经不是一天两天了,大部分C语言老师在讲到goto这关键字的时候一般都会叫大家慎用goto关键字。既然goto语句C语言标准中定义了,那为什么不建议使用呢?
因为goto语句不仅让代码的可读性很差,随意的跳出还会给程序带来安全隐患。但正是这种几乎被各大公司明令禁止使用的语句,在Linux内核中却被大量使用着。
这只能说明一点,那就是因为我们水平太菜了,公司怕因为你的一句goto造成代码莫名跑飞!
早期的程序员用goto来解决代码无法预料的后果,遇到什么问题就用一句goto,让程序跳转到某个指定语句。
但是如果你的水平不够,不能完全理解整个代码的执行过程的话,贸然使用goto就可以出现莫名的问题,并且程序还很难被查到!
不建议使用goto语句的原因还有以下几点原因:
goto语句可以被结构化程序的别的语句代替;
goto语句会导致程序可读性下降,因为在实际程序中,goto可以跳到任何地方,可以往前可以往后,看程序慢慢看,看到goto然后又要去找标识符到底跳到了哪里,可读性严重下降,让读程序的人很不舒服;
调试不舒服,调试程序时,由于有goto乱跳的,这就很难调试,去掉嘛,要重新写代码,不去掉,无从下手;
存在即合理,goto语句也不例外,goto它存在,确实在某些程序中使用可能有好处,但在我们学习的阶段,应该尽量不要碰这类程序,养成一个好的编程习惯。记住一句话:**别人写的goto我能看懂,但是我自己不会去写goto!**
三、慎用extern关键词在C语言程序中,我们用extern关键字对某个变量作 “外部变量申明” ,表示该变量是一个已经定义的外部变量,编译器就会自动地在所有源文件里面查找该变量的定义。
但是在公司编程规范中有明确要求:不允许在C文件中使用 “extern” 来申明外部函数或全局变量
具体原因如下(其中一点原因):
这样使用extern来定义全局变量确实能给我们带来了很大的便利,从而节省了我们很多的时间和精力。但是这样做也会存在一些危险,比如我们在c3.c文件引用的在a1.c文件的funca函数原型由UINT funca(UINT uiValue)变为UINT funca (UINT uiValue1, UINT uiValue2)我们在编译的时候不会报错,但是在我们执行程序的时候会在使用该函数的时候存在危险,尤其是该函数若有一个参数为指针,极有可能会存在对指针的误操作,而引起异常。
嵌入式特别是单片机的程序,最易犯的错误是全局变量满天飞。此现象在早期汇编转型过来的程序员以及初学者中常见,这帮家伙几乎把全局变量当作函数形参来用。
每当看到这种程序,我总要戚眉变脸而后拍桌怒喝。没错,就是怒喝,我不否认全局变量的重要性,但我认为要十分谨慎地使用它,滥用全局变量会引申带来其它更为严重的结构性系统问题。
滥用全局变量会造成不必要的常量频繁使用,特别当这个常量没有用宏定义“正名”时,代码阅读起来将万分吃力。
会导致软件分层的不合理,全局变量相当于一条快捷通道,它容易使程序员模糊了“设备层”和“应用层”之间的边界。写出来的底层程序容易自作多情地关注起上层的应用。这在软件系统的构建初期的确效率很高,功能调试进度一日千里,但到了后期往往bug一堆,处处“补丁”,雷区遍布。说是度日如年举步维艰也不为过。
由于软件的分层不合理,到了后期维护,哪怕仅是增加修改删除小功能,往往要从上到下掘地三尺地修改,涉及大多数模块,而原有的代码注释却忘了更新修改,这个时候,交给后来维护者的系统会越来越像一个“泥潭”,注释的唯一作用只是使泥潭上方再加一些乌烟瘴气。
全局变量大量使用,少不了有些变量流连忘返于中断与主回圈程序之间。这个时候如果处理不当,系统的bug就是随机出现的,无规律的,这时候初步显示出病入膏肓的特征来了,没有大牛来力挽狂澜,注定慢性死亡。
无需多言,您已经成功得到一个畸形的系统,它处于一个神秘的稳定状态!你看着这台机器,机器也看着你,相对无言,心中发毛。你不确定它什么时候会崩溃,也不晓得下一次投诉什么时候道理。
然后,我告诉大家现实层面的后果是什么。
“老人”气昂昂,因为系统离不开他,所有“雷区”只有他了然于心。当出现紧急的bug时,只有他能够搞定。你不但不能辞退他,还要给他加薪。
新人见光死,但凡招聘来维护这个系统的,除了改出更多的bug外,基本上一个月内就走人,到了外面还宣扬这个公司的软件质量有够差够烂。
随着产品的后续升级,几个月没有接触这个系统的原创者会发现,很多雷区他本人也忘记了,于是每次的产品升级维护周期越来越长,因为修改一个功能会冒出很多bug,而按下一个bug,会弹出其他更多的bug。在这期间,又会产生更多的全局变量。终于有一天他告诉老板,不行啦不行啦,资源不够了,ram或者flash空间太小了,升级升级。
客户投诉不断,售后也快崩溃了,业务员也不敢推荐此产品了,市场份额越来越小,公司形象越来越糟糕。
注:以上关于extern的建议来源于黄工的分享。
四、慎用指针指针对于初学者来说本来就是一个不易理解的东西,初学者一般都不能够真正的理解指针,并且正确的使用指针,下面是初学者常犯的错误:
空指针: 指针值为NULL的指针叫空指针,不能运行解引用,一旦解引用空指针就会产生段错误。
NULL在大多数系统的值为0,该地址储存操作系统重启的数据。
NULL也被当作错误标志,如果函数的返回值是指针类型,当它的值是NULL时说明执行出现错误。
如何避免空指针产生的段错误:对来历不明的指针进行解引用前要先判断是否为空
野指针: 指针变量的值是不确定的,随机的,未知的,这种指针被称为野指针。
对野指针进行解引用的后果:一切正常 (运气好)、段错误 (大概率)、脏数据 (堆内存申请的越多,脏数据可能性越大)。
终结出来还是那句话:**别人写的goto我能看懂,但是我自己不会去写goto!**
五、编程规范我在这里给大家分享一写我们公司的编程规范,大家可以学习一下!
- 不允许在C文件中使用“extern”来申明外部函数或全局变量;
- 禁止使用八进制数;
- bit位变量移植性差,应避免使用。推荐使用boolean类型;
- bit fields位域变量移植性差,不应使用;
- uint, sint使用机器字长,虽然速度快,但有溢出风险,应避免使用;
- 指针的数学运算只能用在指向数组或数组元素的指针上;
- 指针减法只能用在指向同一数组中元素的指针上;
- 数组的索引应当是指针数学运算的唯一可允许的方式;
- 不应在指针类型和整型之间进行强制转换;
- 不应在某类型对象指针和其他不同类型对象指针之间进行强制转换;
- 如果指针所指向的类型带有const 或volatile 限定符,那么移除限定符的强制 转换是不允许的;
- 数学运算时,应有效防止数据溢出;
关于编程规范的问题其实还有很多需要注意的事情,如果大家感兴趣的话可以搜索一下网上总结好的编程规范范文,尤其是初学者,在最开始就要养成一个良好的编程习惯,不理解的东西就尽量不要使用!
最后,愿读到这篇文章的程序员们写的代码永无bug!