2006年五月月 发布的文章

代码评审很必要

记得刚到公司做第一个项目时,mentor要和我一起看看我刚实现完的一些代码,当时有些不解,难道是不相信我写的代码么?最后事实证明:我的代码中有很多缺陷,有的还是很严重的缺陷。后来知道这个过程叫’代码评审’,是保证软件质量的一种手段,而且是很重要的一种手段。代码评审的形式有多种,最正式的一种就是召集公司或者部门的一些’大牛’们,围坐在会议室中,一行一行的审查你的代码;简单的形式就像我和mentor做的那种,一个编写代码的人和一个对系统特别了解的人在一起评审,效果不见得不如正式的评审,起码我是这么认为的。

那么什么时候进行代码评审呢?代码评审的粒度又该如何确定呢?一般在你的代码已具雏形,并且已通过单元测试,未提交进行集成测试之前的这个时机。评审的粒度要看代码的规模,当然评审的代码覆盖面越大,代码的质量越有保证。对于涉及业务流程较复杂的模块一定要和熟悉业务的人一起完成代码的评审,对这类模块应给予重点关注,因为往往业务流程逻辑的错误多于语言使用的错误。还有一个时机建议作代码评审,即在系统第一版发布后,如果有需求变动需要增加新功能,记住这时代码评审的效果可能好于简单的测试,当然两者都要有才能保证代码质量更高。

粗略的想了一下,代码评审有以下几点好处:
1、代码评审会尽可能的将Bug杀死在萌芽阶段
实践证明:项目先期发现Bug的成本要远远小于后期发现Bug的成本,越早发现代码中的问题对整个系统的控制和把握就越有利。

2、代码评审的过程是一个知识技能传承的过程,有利于新人成长
代码评审类似于师傅手把手教徒弟,是知识、技巧和经验的直接传授,所以这样的机会是很难得的,特别是对于新人,要十分珍惜代码评审这样的机会,新人在这个环节中学习的效率和成果实践证明也都是最高的。

3、代码评审会让你加深对系统的理解
代码评审的过程实际上也是从新梳理思路的一个过程,特别是对于那些业务流程复杂的模块,和精通业务的人一起做代码评审可以让你加深对业务和系统的理解。

总之,代码评审是必要的,而适当的加大代码评审的粒度,你将会收到意想不到的效果。

说在端午,吃在端午

又是一传统佳节-端午,每逢节日,大家的能做的无非两种:说(祝福)和吃,我在这篇的标题中已经包含了这两部分了^_^。

好像今年早些时候听说韩国人把’端午节’抢注了,噢,对了,不叫抢注,叫抢先’审遗’了,今天又有新闻说:中国人花了3万美金从韩国人手中买回’端午节.cn’的域名,作为中国人我当然是很气愤了,真想大骂韩国人无耻。不过回过头一想谁让我们的政府不好好保护老祖宗留给我们的大好遗产的呢,长此以往重阳节、春节、元宵节将来都得被抢光了,东亚这些国家哪个不是’虎视眈眈’的,最差的情况就是以后过节都要交’专利’费了^_^。

哪年端午都吃’粽子’,哪年端午吃的粽子都不如今年多。早在上一周各大超市就开始了’粽子战’,在强大的广告宣传攻势下,我也’屈服’了,买了为数不少的,种类繁多的小粽子,有猪肉的、牛肉的、叉烧的等等我爱吃的肉馅的,素的和最传统的那种不带馅的我不爱吃。今天才是端午节正日子,一早去食堂吃饭,本打算买两个肉包子吃,不料又赠送一个粽子和一个鸡蛋,一大早哪吃得下呀,决定留在中午再吃。中午去食堂吃饭,又被赠送了2个大粽子,三个粽子、一个鸡蛋,这下算是解决午饭问题了。虽说都是我不爱吃的那种不带馅的,但权当白米饭了,吃了再说,就这样三个粽子下肚了。中午接到GF电话,说她也买了若干粽子准备晚上到这来和我一起吃,晕倒。看来今天三顿饭是离不开粽子了。
‘粽子曾可贵,GF价更高’呀,她买的哪敢不吃。但愿晚上做梦千万不要再吃粽子了。^_^

祝大家2006端午节快乐!

小心库函数调用的'陷阱'

下午一同事发现代码中的一处问题,问题的现象是这样的:这位同事调用了一部门基础库函数,当使用32位编译后,程序正常运行;而当使用64位编译后,系统运行dump core。让这位同事奇怪的是他所修改的程序中还有其他模块也使用了同样的基础库函数,为什么偏偏他这块儿出错呢?恰恰该程序的其他模块是我写的。

该程序调用的基础库函数大致是这样的:
typedef unsigned long my_size_t;
int my_socket_recv(my_socket_t socket, char *buf, my_size_t *len, int timeout);

而这个库函数的实现主要就是调用了系统的库函数:recv,通过man命令查到recv函数原型为:ssize_t recv(int s, void *buf, size_t len, int flags);

而my_socket_recv的实现如下:
int my_socket_recv (my_socket_t socket, char *buf, my_size_t *len, int timeout)
{
     int rv;
     … …
 
     rv = recv(socket, buf, (*len), 0);
     … …
}

使用gdb察看core文件,通过栈上信息看出(*len)值有问题,有经验的人可能基本就可以断定问题所在了,对了,应该类型长度不匹配的问题,导致内存访问越界。同事的代码也证实了这一点。原来我的这位同事在调用my_socket_recv时第三个参数传入一个unsigned int型的地址,而不是unsigned long型的地址,我们知道在64位编译下,long型已经升级到8个字节,而int型依旧是4个字节,而size_t也是unsigned long的typedef,所以当调用recv时,通过len指针,recv系统调用毫无顾忌的去取8个字节,而实际上在栈上只有前四个字节是合法的数据,从第5个字节开始已经是非法的了,出core也就在情理之中了。我们可以简单的模拟出这种情况,如下面的例子:

/* test.c */
#include

void print_long_int(unsigned long *i) {
        printf("i is %ld\n", *i);
}

int main() {
        unsigned int n = 1024;
        print_long_int(&n);

        return 0;
}

64位编译:
gcc -m64 -g test.c

执行a.out出core.

问题虽然解决了,但是思前想后,发现一个问题:即库函数调用暗藏的’陷阱’问题,像这样的问题实际上是基础库函数的接口设计的不够好,实际上它的正常运行依赖某些条件,而不是’无偿’的,但是这些条件又很难识别出来,特别是对于新手而言更是难上难。

想出两种办法解决:
1、不要设计像上面my_socket_recv这样的带有’隐含’条件的接口(recv系统调用接口就没有隐含条件);
2、调用接口的人不要想当然的随便传入不同类型的数据,应尽量保持实参数据类型与形参数据类型完全匹配(这块儿编译器可以帮你做检查),如果不这样,很有可能像我的这位同事一样,掉入了库函数调用的陷阱中。^_^




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多