最近将同学推荐的《代码大全》看完了(已经过去一年了,要十分感谢推荐,还有《深入理解计算机系统》)。
零零碎碎的时间,发现很多东西虽然在书中标记了,
但是再去翻看时,还是有很多已经模糊的地方。
想来,还是需要留个笔记,供以后看看。
关于《代码大全》,
每章后边的CheckList都需要好好分析与思考,并能实际运用。
第六章
提出抽象数据类型,是进行模块化(原文为类)的基础。可以让我们只根据当前类型来理解代码,或者操作相应的功能。
良好抽象数据类型,让我们有效地隐藏一些数据以及实现的细节,同时提高程序的可读性,降低程序的复杂度(不用去关心底层具体实现),让程序更容易理解。避免使用全局数据,提高程序的可重用性。
第七章,
高质量子程序
何为子程序?
子程序是为实现一个特定的目的,而编写的一个可被调用的方法或者过程。发明子程序的作用,节省空间,提高性能。
子程序的作用,
可以降低复杂度,不必去关心子程序的具体实现。
避免代码重复,都是提高修改程序的效率(因为代码只出现一次,所以只在一处修改就可以)。
隐藏操作顺序,
隐藏指针操作,
提高可移植性,隔离硬件无代码和硬件底层代码,减少移植时需要修改的工作量。
对于子程序而言,提高子程序的内聚性是关键,而功能性的内聚性,是最有效的内聚性。
同时,好的子程序名字,提高程序的可读性,使代码更易读。更多的时候,阅读代码的次数,远远大于编写的次数。
描述子程序所做的事情,
避免使用无意义的,模糊的动词。
不仅仅通过数字来星辰不同的子程序名
合适的子程序长度一般在200以内,偶尔长的子程序,尽量不超过500行。
对于子程序的参数,尽量按照同样的顺序出现在同一个工程中,同样不把输入参数,用作工作变量(这个是经常做的事情)。使用所有输入参数。
对于宏和内联子程序,尽量不用。不到万不得已不使用,如果使用,按照子程序的方法来使用。
创建子程序的原因,
提高可读性,可靠性,可修改性,节省空间(一个次要的原因)。
第八章,
防御式编程,
原则,保证垃圾进,正确出。
检查输入参数,是最有效的方式。
其中断言是最好检查输入参数的方式。对于断言的使用要讲求方法,同时在正式代码中要避免存在断言。
在看到断言的时候,想起来自己之前使用断言的方式,
突然发现有些问题。
那个时候,总希望断言能够判断并决定是否返回值,或者不返回。
其实这里,只需要进行判断就行了。对于具体的处理,
由程序代码处理,
是返回值,或者不返回值。
当然,这里就需要,断言只是判断的代码,而没有执行的代码。
对于健壮性与正确性,是一对矛盾,根据需要,
合适的时候,选择合适的处理平衡。
断言处理,函数的前条件和后条件。
当然,对于错误处理的方式,有很多种,也需要根据需要,
选择合适的方式。
对于健壮性要求,和正确性要求,他们适合的方式,不一样。
工业类软件,及要求健壮性,也要求正确性。
要同时保证这两个。
如果可以,将断言单独做一块处理。
保证进入子函数的数据,都是正确的。
区分开发代码,和产品代码。在开发代码中,尽量采用进攻式编程,找出尽可能多的错误。
在发布的产品代码中,尽量使用防御式编程。
分级式防御。
第九章
对于伪代码编程,感觉更多的是编程前的构建,当然好的伪代码编程,可以直接将伪代码转换为注释。
对于程序的性能优化,最好的还是从高层进行优化。单独的对于一个函数,一个子程序进行优化,所起到的作用,都是有限的。
做到从迷信到理解的转变。
创建子程序是一个迭代的过程。在创建子程序的过程中,获得的认识常常会反过来影响子程序的设计。
在编程完成后,立即检查代码的成本是最小的。
第十章
关于变量的命名,使用以及初始化等内容,应该是很好的一段帮助提升代码可读性的内容。代码的逻辑性,本就复杂,如果代码的变量能够一定程度上指示此时的操作内容,可能会一定程度上提高代码的自说明性。
因为代码写的是给人读的程序,而不是给机器读的程序。
几个原则:
变量的初始化,是很关键的一项。变量在靠近第一次使用的时候初始化,既可以减小变量的作用域,又减小代码跨度,提高程序的阅读性,减少同一时间考虑的代码量。查看编译器警告,消除未初始化的变量。
因为程序读的次数,要比写的次数多,所以尽可能写出,容易读的程序。
灵活性与复杂性是一个对矛盾。绑定时间晚的程序灵活性大,但是复杂度高;绑定时间早的程序灵活性小,但复杂度低。
第十一章
好的变量名,反映的是问题,而不是解决方案。
好的变量名,是提高程序可读性的一项关键因素。
对于状态变量的命名,尽量采用能够反映状态的名称。
对于命名规则,有很多种,并没有最好的规则。采用适合的,团队使用的。
命名规则应该你呢够区分,局部数据,类数据和全局数据局。
代码阅读的次数,远远超过编写的次数。
第十二章
感觉这一章提到了很多平时忽视的细节。
尽量使用具名常量。
预防除零的情况。
C中,尽量使用强制类型转换。
避免不同类型之间的比较。同时对于浮点型的比较,不能使用等量判断,只能使用一定的精度值。
注意编译警告。
整数:
注意整数除法。
检查整数溢出,uchar型注意不超过255,uint16,注意不超过65535
检查计算中间的结果溢出。
浮点数:
避免数量级差别大的数之间运算。
避免等量判断。
处理舍入误差问题。
字符和字符串
使用具名常量,来代替神秘字符和字符串
避免off-by-one
避免使用不安全的字符拷贝函数,尽量使用strncpy(),等安全的字符串函数。
尽量使用具名常量
数组
避免下标越界
注意数组边界,避免off-by-one
在C中可以结合ARRAY_LENGTH()宏来判定数组长度。对于指针无效。
使用typedef,
第十三章
这一章,涉及了我们初学时,不经常接触,但是实际用时,却经常用的,
指针,结构,全局数据
在C,中可以将结构体当做“类”一样来使用,进而通过C,实现面向对象编程的思想。当然,会麻烦一些,但是对于所带了的可读性与可复用性,绝对是是值得的。
当然,结构体也有自己的优势:
明确数据关系,使得代码更易阅读
简化了对于数据块的操作
同时可以简化函数的参数列表(尤其当有很多变量需要传递时),
为以后的维护代码,减少工作量。
对于指针的运用尤其固有的复杂性,正确的使用指针要求你对所有编译器的内存管理机制有很好的理解。
指针的两部分:内存中的某处位置(内种中的地址),如何解释该位置的内容(指针的基类型)。
关于指针错误很难发现,也很难调试,
所以首先要避免造成指针错误,
其次编写代码后,尽快检测出指针错误。
几点方法:
把指针操作限制在子程序里,
同时声明和定义指针
在与指针分配相同的作用内删除指针(实际中,这种情况经常被违反)
使用前检查指针,
先检查指针所引用的变量,然后使用之
先填充无效数据,然后再释放的内存,
增加明显的冗余
用额外的指针变量,来提高代码清晰度。丛长久而言,代码的清晰度,远比代码的执行效率要重要。更何况,有时候这样做,还会提高执行效率。
简化复杂的指针表达式
按照正确的顺序删除链表中的指针
分配一片保留的内存后备区域
粉碎垃圾数据
在删除或者释放指针后,将他们设置为空值
在删除变量前,检查非法指针(指释放已经释过的指针,很可能会造成程序崩溃)
跟踪指针分配情况
采用非指针技术(记得以前考C二级的时候,指针还不怎么会用,就都用数组来替,竟然也都没有问题)
对于C指针而言的技巧
使用显示指针类型
避免强制类型转换
遵循参数传递的型号规则
在内存分配中使用sizeof()确定变量的大小(sizeof在编译时执行)
对于全局数据的使用要慎之又慎,只在万不得已的时候使用全局数据
全局数据减弱了模块化,增加了 耦合度,阻碍了代码重用,增加了复杂度(正好背离的软件的出发点,降低复杂度)
在使用全局数据时需要注意的:
首先吧每一个变量设置为局部的,仅当需要时才把变来那个设置为全局的。
区分全局变量和类变量
使用访问子程序(使用访问子程序还有需要需要注意的地方,这个慢慢消化)
结构体是程序更简单,更易理解,更易维护
指针很容易出错,
避免使用全局。
附:
《代码大全》推荐的几本书:
入门类:
《代码大全》
《编程珠玑》
《Software Project Survival Guide》
提高类/专业类:
《Rapid Development》
《编程精粹:编写高质量C语言代码(中文版)》
《重构:改善现有代码的设计》
工程类:
《软件工程:实践者的研究方法》
对于这些书的内容,大多数是不分语言的。
作者在《代码大全》中也一直强调深入一种语言去编程,而不是在一种语言上编程。
正确的时候,读合适的书!
网友1:这些原则都是很容易理解的,但只有大量的编程实践才能熟练掌握。
作者回复:这些确实是需要编程实践来感受。 很多时候,我们过于实践而没有回来总结与反思。而这些书,正好将我们可能想到了,没有想明白的,未来会遇到的都写下来了。 现在,感觉如果边实践,边看这些,已经掌握的,可以更深入一层思考。没有掌握的,可以再以后遇到时,思考的更深入。
网友2:哇,可敬,不容易啊,那么厚的一本书看完了,感觉第一版比第二版似乎易读一些。
作者回复:第一版没有读过呵。 这本书,感觉有些指引性。 除了中间介绍的编程风格及技巧,还有很多提到了,但是没有深入展开。它推荐的那些书,应该是我们深入的有一个阶梯。