个人作业Week2-代码复审
一、代码复审部分
1. 概要部分
代码能符合需求和规格说明么?
代码基本符合需求和规格说明,但是在程序中有多余的Debug信息(sudoku.cpp中的176行)
代码设计是否有周全的考虑?
有必要的错误处理,但是还不太完全,有错误检查没有覆盖的地方会引发程序异常。其他的设计还比较周全。
代码可读性如何?
可读性还可以,变量的命名有一定的意义。但是注释太少,并且由多个单词组成的函数/变量名并没有进行大写区分使得并不容易快速明白函数/变量名所表达的意义。另外程序中空行过多,导致程序读起来并不太舒服。
代码容易维护么?
由于注释比较少,可读性并不太好,并不太容易进行维护。
代码的每一行都执行并检查过了吗?
检查过了。
2. 设计规范部分
设计是否遵从已知的设计模式或项目中常用的模式?
采用的是比较普通的业务逻辑以及解体思路,并没有使用特定的模式进行设计编码。
有没有硬编码或字符串/数字等存在?
有比较多的硬编码数字。比如生成大量用的数独模板是硬编码的二维数组。
代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)?
没有平台依赖,只要是windows都可以运行。
开发者新写的代码能否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
开发者新写的代码并不能用已有的功能实现。
有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删除,因为源代码控制已经保存了原来的老代码。)
没有无用的代码可以消除,但是有一些冗余代码可以进行合并进行代码简化。
3. 代码规范部分
修改的部分符合代码标准和风格么(详细条文略)?
-
函数名变量名如果由多个词拼成,应该大写首字母进行分割,便于快速理解含义。开发者的代码中(例如canin函数应该是canIn更符合规范)就没有遵循这点。
-
多余的空行实在太多,有的连续几行都是空行,可以都删减掉。
-
程序最开始的函数声明可以放到单独的头文件中,以增加程序的复用性以及可读性。
其余代码标准和风格都比较符合规范。
4. 具体代码部分
github的comment地址:https://github.com/zhj123169/bin/commit/967e00868b45d023dc934a4f388462fe519073a7
有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常?
在程序中有对输入参数以及调用的外部函数的错误处理,并输出了错误信息。如果要求高一点的话,对于输入参数检查的错误处理的错误信息输出并不明确,以后可以进行错误信息的细化方便在运行时输出错误信息时可以快速定位出错误发生地点。但是在参数错误处理上还是有些错误分支并没有覆盖全(例如输入运行指令为 sudoku.exe -c,没有指定生成数独的数量,但是该情况会认为是正确的仍然想继续进行参数解析,会发生不可预知的错误行为)。
参数传递有无错误,字符串的长度是字节的长度还是字符(可能是单/双字节)的长度,是以0开始计数还是以1开始计数?
参数传递没有错误,是以字符的长度作为字符串的长度,从1开始取出参数进行解析。
边界条件是如何处理的?Switch语句的Default是如何处理的?循环有没有可能出现死循环?
数组大小基本为99的,也有9的,由于边界是定死的99的,所以正常的循环访问就可以确保不会越界。没有Switch语句。循环没有可能出现死循环。
有没有使用断言(Assert)来保证我们认为不变的条件真的满足?
没有使用断言来保证不变的条件真的满足。
对资源的利用,是在哪里申请,在哪里释放的?有没有可能导致资源泄露(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有可能优化?
这个数独软件使用的资源为文件资源和内存资源。文件资源方面在进行读/写操作时进行申请,并在使用后及时进行了释放,没有文件资源泄露问题。
对内存资源的利用是在函数体内申请,但没有释放的地方,有非常严重的内存泄漏问题。具体的内存泄露问题是在大量调用的transform函数体内进行了内存申请,为每个生成数独申请了内存申请,但是却将指针赋给了调用方的临时指针,导致了严重的内存泄漏,使得程序后来的内存回收也比较困难(虽然程序最后也没有释放内存)。
内存泄露的优化有两种,一种是在调用transform函数将原来的指针替换掉之前检查原来的指针是否为空,如果不为空则释放内存,再调用函数进行指针替换。
if(sudokuout!=NULL) free(sudokuout); // add to avoid memory leak
sudokuout = transform(oralline,oralsudoku);
或者在程序刚开始为每个数独分配好内存空间,在程序最后进行统一的内存释放,而不是在函数中分配内存空间。
数据结构中是否有无用的元素?
现在的数据结构是简单的二维表,没有多余无用的元素。
5. 效能
代码的效能(Performance)如何?最坏的情况是怎样的?
现在代码的效能还是比较差的。最坏情况下,也就是生成100w以及求解100w个数独时,耗时都大于420s。
代码中,特别是循环中是否有明显可优化的部分(C++中反复创建类,C#中string的操作是否能用StringBuilder 来优化)?
代码中有两处可以明显优化:
- 在transform中反复调用了malloc函数,这个会导致内存泄露的同时也是没必要的在循环内的反复申请内存空间。可以在程序刚开始就把所有数独的空间都申请到,最后再释放。
- 判断一个数能否填入空白时,用循环遍历的方式来检查是否有重复是比较低效的,可以通过实时记录某行/列/块的某个数字是否填入(bool类型)来进行快速查重,这样可以每次最内层循环的至少27次比对操作就减少为最多3次比对操作,大大提高运行速度。
对于系统和网络调用是否会超时?如何处理?
并没有系统调用和网络调用。
6. 可读性
代码可读性如何?有没有足够的注释?
可读性不太好,注释几乎没有,变量/函数名可以一定程度上说明功能,但还是不太充分,需要更多的注释进行解释来增加可读性。另外过多的空行也影响了阅读体验。
7. 可测试性
代码是否需要更新或创建新的单元测试?
在github上的代码中没有上传单元测试,需要增加测试代码。
还可以有针对特定领域开发(如数据库、网页、多线程等)的核查表。
程序没有对特定领域的开发。
二、 设计代码规范
工具提供的代码规范和你个人的代码风格有什么不同?
1. 不使用using来引入某个命名空间。
2. 变量的初始化和声明要放到一块。
3. 每行末尾没有空格
工具提供的代码规范里有哪些部分是你之前没有想到的?
1. 不使用using来引入某个命名空间。
2. 变量的初始化和声明要放到一块。
3. 每行末尾没有空格
为什么要这样规范?这样的规范有意义吗?
这样规范代码可以保证较高的代码可读性,并减少自己或者多人合作导致的代码错误,提高了工作效率。例如上面的不使用using来引入某个命名空间可以有效避免命名空间中的某个函数与自己的某个函数产生了命名冲突导致的异常,等等。
最后附上我们在结对编程时使用的代码规范:
(1)应该用4个空格代替tab
(2){应该和代码在同一行,并且中间有一个空格
(3)else,else if应该与if的}在同一行,且中间应当有一个空格
(4)注释的“//”和代码不能直接相连,中间应该有空格
(5)不能够使用using来引入某个命名空间
(6)每行的末尾不应该有空格
(7)应该有copyright
(8)代码的中间不应该有无关注释和无用空行
(9)函数结束后应该与下个函数间空一行
(10)过多的函数声明可以放到单独的.h头文件中
(11)变量名如果由多个单词组成,从第二个起第一个字母应当大写
(12)避免无用函数依旧在代码中出现。
(13)不要使用goto语句
(14)不要将多行代码放在同一行(比如多个变量声明)
(15)变量的声明一定要有初始值
(16)变量的初始化和声明要放到一块