读书笔记

高质量程序设计艺术

2008年5月17日 阅读(226)

笔记

最近看了下<<明朝那些事儿>>,前四本是在图书馆发现的,剩下的部分则是在网上看的,说的是明朝300年的历史,文字比较平民化适合没事的时候看看,具体内容不说了,主要是里面的牛人云集,牛人们办的事也是相当牛。

当然众多牛人中,最牛的人当属王阳明,文治武功,思想品格纵观中华历史,鲜有人比,很多牛人某方面做的很好,恐怕只有他不但开宗立派,用兵更是出神入化,计谋才智一流,而且以天下为己任,待人接物更是无可挑剔,总之应该是中国历史上兼具理论和实践的天才人物,后人说他的才能已超过诸葛亮。。。

所以呢,顺便看了下王阳明的个人传记(<<旷世大儒 王阳明>>)和作品(<<王阳明全集 知行录>>)。

纵观阳明一生,所历颇多,少时便有豪气,行为不羁,虽科举落第,并不以为意。先求诗文,后研佛道,格物穷理,龙场悟道。平定叛乱,教化万民,功延后世,及至所行遭妒,朝廷无纳贤之意。当国危难,始忆昔之阳明子。然阳明不以往昔为由,身犯险境,安抚叛乱,后返乡途中病危。弟子问"师复何言?",乃曰"吾心光明,亦复何言",遂亡。

————————————————————————————————————————————-
当然啦,以上是历史啦,缅怀历史的同时,还要好好学习啊

于是开始看<<编程卓越之道>> <<问题求解方法:现代启发式方法>> <<高质量程序设计艺术>>。前两本主要是大体浏览一下,以后有空再细看吧。

这后一本,是位希腊人写的,他写过的另一本<<代码阅读方法和实践>>获了2004年的jolt生产效率大奖,看了感觉一般。2007年的这本,估计作者是看了不少论文之后写的,都是以往人们很多经验的总结和历史的观点。

anyway,看了感觉还是不错的。

——————————————————————————————————————————————

笔记:

本书主要介绍了高质量的程序设计,当然是从代码一级上阐述的,主要从下面的角度来看:可靠性,安全性,时间性能,空间性能,可移植性,可维护性,浮点运算。

下面以实例说明:
1.格式化字符串漏洞
char temp[10];
scanf("%s",temp);
printf(temp);
本身对于scanf便会面临缓冲区溢出的问题,当然这个程序里面还面临另外一种问题:如果输入串为"%d",则printf就会暴露程序的数据,如果精心安排会导致安全问题。
应该写成printf("%s",temp);

2.内存分配
一个实际中的症状:在解压文件时,当文件数目在38000以下时,程序可以快速的完成解压。可是当文件数目在40000时,效率突然急剧下降,请问原因是什么呢?

原因是在38000可以完全装入内存,可是达到40000超出容量,发生了频繁的主存替换。

内存分配常见的问题有内存泄露,悬空指针以及缺少对返回结果的检查。内存分配的过程是这样的,应用程序调用malloc程序,而malloc程序会向操作系统预先申请一大块内存(相当于缓存的原理,因为如果每次申请都要跟os沟通,会产生大量的切换开销),然后malloc自己管理,如果还有可以分配的资源则进行分配,否则向os申请。当然我们一般会使用库函数的malloc,也可以写自己的malloc。

内存是否会耗尽呢?
答案是肯定的,操作系统检查可用空间,发现没有足够的主存了,这时它会将目前未使用的内容转入交换分区。但是交换分区的大小也是有上限的,极端情况下,当交换分区也耗尽的时候,os就宣告内存申请失败,这时malloc也会失败。当然即使不耗尽,内存泄露也会造成频繁的换入换出而使程序效率变低。

同一个程序两次运行,malloc返回值是否一样?
是的,在支持虚拟内存的os中,这两次是完全一致的。不过由于受具体环境影响,可能失败的时间不同。

内存泄露是指你分配了内存但是后来丢失了它的指针。如a = relloc(a,n)便极有可能产生泄露,原因如下:当relloc失败时,返回了NULL,于是a原来的值就丢失了,无法释放。

malloc碎片产生原因:孤立消失,不可移动对象,时间因素

垃圾收集机制:对于java来说具有自己的垃圾回收机制,当某对象不用时,最好显示的置为NULL,这样方便回收。

对于c和c++垃圾回收是需要程序员自己记住的?
当然也存在一种保守的垃圾回收机制:遍历数据区数据,与堆管理器交互,检查该变量是否是已经分配的一个指针,如果是则说明该内存还有指针指向,将其标志为非垃圾。
问题:如果内存的指针保存在文件而非内存或者以加密方式存储则可能产生问题。

3.栈

需要保存和恢复寄存器的值。

参数入栈方式:从右往左。

原因:因为栈指针是指在栈顶位置,也就是说栈顶地址是已知的。对于某些可变参数的函数来说,这样便可以在距离栈顶固定距离处找到第一个参数。

4.浮点运算

两个个事实:
很多十进制分数是无法用有限二进制表示的,只有那些分母的因子只含有因子2的才可以。比如1/10,1/5比然无法用有限二进制表示,即使存在无限的二进位可以让你用。

计算机的浮点表示,必然是不连续的,相邻两个double浮点数的距离(ULP)有时甚至可以达到8912。

几个问题:

溢出:
上溢-解决方法:重新安排计算顺序,乘以一个因子。sqrt(x^2+y^2)->sqrt(sx^2+sy^2)/s
下溢-太小时,会下溢为0,可能产生除0

反向规格化:阶码为0。
目的是为了可以使表示的数字可以尽可能接近0。如0.000049*2^-1023,不用反向规格化是无法表示的。

相等 :浮点数的相等比较是用EPSILON实现的,不能直接使用==

相消:发生在两个极为相近的数相减的时候,原因在于浮点数的有效数字是有限的。当相减的时候会使有效的数字消失,只剩下无效的数字位。

如:10^6-99999.99999599997,误差远大于ULP/2(这个数的最低有效位的值,实际上也是离他最近的下一个浮点数的值)。再如b – sqrt(b^2-4ac),如果ac很小就会如此。

吸收:很大的浮点数与很小的浮点数运算有可能吸收很小的浮点数。原因仍然在于浮点数的有效数字是有限的。

如:(255+o)(1.1+o)o很大时,1.1,255便不在起作用。再如:计算面积的海伦公式area = sqrt(s(s-a)(s-b)(s-c)),s = (a+b+c)/2,当其中两边远远大于其中一边时会出现吸收。
解决方法:
将abc排序,a>b>c
area=0.25*sqrt((a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)))

比较大小:可以直接按照内存位置升序进行比较。符号位->阶码->尾数。

5.风格
是个大问题,平时要注意。

You Might Also Like