您的位置:首页 > 其它

经验确实很重要

2016-04-22 15:19 309 查看
    为什么你写的代码好好的,在别人机器上就运行出错呢?,这大概是所有的程序员最纠结的事情了。最近在开发新模块也在测试修改遗留的bug,稍微积累了一点心得:对程序员来说,经验真的很重要。(难怪很多公司都要招有一定工作年限的程序员)好了,详细说说。    

    我最初比较有印象的一个bug是这样的。

    现象:一个月前有一个项目,里面有多个工程,但他们互不影响。假如我需要在工程A中引用工程B中的一个函数,则要么将A中的这个功能制作成DLL,要么将这部分代码,看懂然后不厌其烦的写一个。好吧我选择后者。毕竟多几个dll在用户的安装路径下谁能保证将来不会有某些用户删除呢?!一切进展的很顺利,但是一个WindowsAPI函数报错了:GetNativeSystemInfo(XXX);(各位可以看我最近发的帖子)。很纳闷,在B工程都没问题,同一台电脑,同一个头文件,为什么会错!!!然后我搜了一下,换成了GetSystemInfo(XXX),没问题,杠杠的!但是,这个API它可能会获取到错误的信息(详情百度)。当时和其他人讨论了一下这个问题,领导的意见是:就用这个,当获取的信息是错误的时候肯定Windows都更新到N版本了没我们的事,先发布再说,短时间内没问题。好吧,就这样把这个模块完成了。一个月后,测试那边告诉我,为什么检查出的数据都是32位的系统,这不可能啊!然后……就是这个api用错了!

    问题根本原因:原来GetSystemInfo(XXX)这个API在获取64位系统的位数的时候是会出错的。然后我想用回那个编译报错的GetNativeSystemInfo(XXX)。详细找了一下出现这个错误的根本原因,发现:这个API必须在这个条件下使用:

#if _WIN32_WINNT >= 0x0501

WINBASEAPI

VOID

WINAPI

GetNativeSystemInfo(

    __out LPSYSTEM_INFO lpSystemInfo

    );

#endif

但是程序中的 定义的是#define _WIN32_WINNT 0x0500 。至于说定义这个数值的原因是为了让软件兼容早起的Windows版本。

    解决方法:不能修改定义好的_WIN32_WINNT 值,但是可以通动态加载(LoadLibrary, GetProcAddress)的方式使用 GetNativeSystemInfo() 获取正确的值(看我的帖子,里面也有解答)。

    这个bug啊,说实话,就是难找根本原因,谁能想到系统每次建立工程后自动生成的stdafx.h文件会被改掉呢?而且这种问题应该说很少见,但是碰到了确实会让人纠结。
 

   第二个bug,还是和上面描述的B项目相关。

    现象:我要去查看用户的office版本。因为A项目也有这个功能,但是以插件的形式做出来的。我还是看了一下那部分逻辑,然后照着样子写了一个出来,诶,测试通过。但是别人反馈给我:有些人电脑上读出来了,有些人电脑上读不出来!奇怪哈,对比一下两份代码发现基本一样,不应该啊!然后看了一下读取成功的用户和读取失败的用户,发现他们的环境变量啊,用户保护等级设置什么的都没有太大出入。这就出鬼了!

    根本原因:在接下来的几天中,我无意中发现,那个A模块的这个功能会有一个弹窗:用户账户控制。我突然想到,难道是因为我的B模块没有获得用户权限个?!跟踪代码后发现,在读取注册表的模块失败了,但是假如我以管理员账户运行我的程序,读取成功!!!

    解决方法:我试着按照读注册表的方法使用部分api而不是去读写注册表,发现window确实在你读取的时候还是宽容的,至少它发现你没有改变注册表键的情况下,没有要求你验证什么。

    就该bug本身,当初自己测试的时候确实没有意识到读取注册表可能会引起权限问题,然后测试的时候获取的数据也符合我的逻辑(没有读取到office版本,也算一个合理的结果)。经过这个bug,我发现其实软件的细节不仅仅在语法和逻辑上,还在运行这些代码本身的机器上,假如我之前碰过这么一个问题,也就不会有这次的bug了。

    

    第三个bug!这一次真是长经验了。

    现象:我有一个模块,在用户界面显示了乱码.整个项目的同事都说我又出问题了(特别是:没准那块内存又越界了),然后加班呗.

    解决方法:我仔细的跟踪管理一下代码,代码中的逻辑显然是正确的,然后又同事在旁边嘀咕了一句:是不是服务端的数据不正确?然后我想啊:我的数据经过了服务端,然后服务端会返回整理后的数据.是不是那边的就是乱码?我去服务端查了一下:果然,就是你妹的服务端down了,返回的是一大堆乱码!当然要是有log的话我也就不用找怎么久了。

    总结:有时候,真的是应该全局的看待一个问题,不要人云亦云(他们都说我的模块就可能问题出现在我的代码里面)!由于本公司的家伙都不喜欢加log功能,导致后期维护人员查一个bug特别困难,因此以后自己开发的项目,要合理的添加log模块.

    

    第四个bug!

    这个bug特别有意思,有一个模块,必须要一些数据返回给服务端,通过jso包返回,然后就是有一个家伙的电脑上发现:他的jso包数据在服务端上总是显示不出来!然后,不好意思,这个模块又是我写的.然后查代码、查逻辑,最后发现,代码和逻辑一点问题都没有啊,传递上去的数据也是合理的。为毛这个人的数据会丢失呢?我只好在服务端查看了一下接受的流程。然后发现额呵:他的数据中有个字符"\uXXXX",但是"\u"被转义了……然后它那边就显示这个人的数据没有收集到!!!!这!怎么说呢?确实可以说是我的问题,但是人无完人嘛,谁能想到的那么仔细呢。

    解决方法:在jso数据上传前,将“\”替换为“/”防止转义!

    经验总结:虽然我不处理由其他模块生成的数据,因为99%的情况下这些数据都是不会有问题的,但是不排除某些用户比较“随便”,比如随便设置一个文件名,导致检测该文件路径的时候出现了问题。所以,下次应该将所有数据中的“转义字符”重新编码!

    

    第五个bug,展示经验和技术的时候到了.

    现象:我有一个操作excel的指针变量,new出来后,在delete的时候发生了heap corruption情况大概类似这个链接描述的样子(http://blog.sina.com.cn/s/blog_4ae178ba0101164d.html);假如换成了普通成员(非指针),用完后,在右括号地方发生了statck corrupted情况类似这个链接中图片的样子(http://blog.csdn.net/chenyujing1234/article/details/8261914)按照他们的方法改,都不行!!!!然后找变量是否被别的线程占用?没有!看变量是否在其他地方释放?没有!然后叫同事过来看一下,同事说,要不你把new
和delete的操作包裹到该变量所属类的地方,然后在使用的时候引用这两个函数.这样操作后,确实没有问题了.

    根本原因:事后,我请教了一下跟我说这个方法的同事.答案类似如下:你在函数的局部new的时候,可能该变量(一个类的对象)的部分内存是在主框架分配的,但是该类部分成员是DLL里面分配的(操作excel确实用到了dll).然后在主框架释放的时候并没有将dll里面分配的内存释放到,导致出现了这个问题,你把new和delete操作包裹成所属类的成员函数,保证了:在哪里分配,就会在哪里释放!妥妥的很有道理啊!

    总结一下该bug:内存在哪里分配,就应该在哪里释放!这句话简单啊,假如没有碰到此类问题,我肯定还是想当然的以为:这句话的意思不就是必须在同一个代码块内处理内存问题嘛.其次,其实早点碰到此类问题是有好处的,保证了下次碰到类似问题的时候多一个思考方向.

    通过上面三个小例子,我确实发现,经验确实挺重要的,也许我有很好的coding功底以及逻辑思维功底,但是,有些问题,确实和这些无关。所依不论是通过别人的例子获取经验还是自己从问题中汲取经验对于从事我们这行的人来说,尤为重要!至少可以保证,当你发现一个问题的时候,会有多一个思考方向,而不是在原来的胡同里面绕来绕去。

    望各位同行共勉!

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  经验