1.代码地址

此代码由赵一霖同学完成,并且已经提交。代码地址为:Coding.net代码地址

2.项目作用

输入有向图的顶点和弧的数据,建立该有向图的邻接表;
实现该有向图的深度优先遍历,并输出结果;
实现该有向图的广度优先遍历,并输出结果。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

3.C++代码审查表

参照博文中的内容设计了如下的C/C++代码审查表,并根据此项目的代码进行审查和填写。

项目名称 有向图的深度和广度优先遍历
审查人 林天成 审查日期        2019/4/22
代码名称 main.cpp 代码作者      赵一霖
文件结构
序号 审查项目 重要性 结论 备注
1 头文件和定义文件的名称是否合理?       
2 头文件和定义文件的目录结构是否合理?       
3 版权和版本声明是否完整?      无版权信息
4 头文件是否使用了 ifndef/define/endif 预处理块? 重要    系统自带的头文件
5 头文件中是否只存放声明而不存放定义       系统自带的头文件
程序版式
序号 审查项目 重要性 结论 备注
6 代码段间的空行是否得体?      正式代码空行得体
7 代码行内的空格是否得体?      代码内空格清晰
8 长行拆分是否得体?       部分较长语句未换行
9 “{” “}” 是否各占一行并且对齐于同一列?        
10 一行代码是否只做一件事?如只定义一个变量,只写一条语句。 重要     部分语句定义了多个变量
11 If、for、while、do等语句自占一行,不论执行语句多少都要加 “{}”。 重要     有些只有一句有效语句的判断
12 在定义变量(或参数)时,是否将修饰符 * 和 & 紧靠变量名? 重要      
13 注释是否清晰并且必要? 重要     
14 注释是否没有错误且不会导致误解? 重要      
15 类结构的public, protected, private顺序是否在所有的程序中保持一致? 重要        未使用类
命名规则
序号 审查项目 重要性 结论 备注
16 命名规则是否与所采用的操作系统或开发工具的风格保持一致? 重要      
17 标识符是否直观且可以拼读?       标识符直观,可读性强。
18 标识符的长度应当符合“min-length && max-information”原则?       
19 程序中是否出现相同的局部变量和全部变量? 重要     部分变量命名重复
20 类名、函数名、变量和参数、常量的书写格式是否遵循一定的规则?       
21 静态变量、全局变量、类的成员变量是否加前缀?       并未有前缀的声明
表达式与基本语句 
序号 审查项目 重要性 结论 备注
22 如果代码行中的运算符比较多,是否已经用括号清楚地确定表达式的操作顺 重要     未出现较为复杂的运算
23 是否未编写太复杂或者多用途的复合表达式?        
24 是否未将复合表达式与“真正的数学表达式”混淆? 重要      
25 是否用正确的方式写if语句? 例如 重要    
1) 不将布尔变量直接与TRUE、FALSE或者1、0进行比较。    未使用bool型变量
2) 不将浮点变量用“==”或“!=”与任何数字比较。      
3) 不将指针变量用“==”或“!=”与NULL比较。      
26 如果循环体内存在逻辑判断,并且循环次数很大,是否已经将逻辑判断移到断移到循环体的外面?       循环体内的逻辑判断简单清晰
27 Case语句的结尾是加了break? 重要       
28 是否写了switch的default分支? 重要       
29 使用goto 语句时是否留下隐患? 例如跳过了某些对象的构造、变量的初始化 重要      未使用goto语句
常量
序号 审查项目 重要性 结论 备注
30 是否使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串?         
31 是否误解了类中的const数据成员?因为const数据成员只在某个对象        未使用类
32 如果某一常量与其它常量密切相关,是否在定义中包含了这种关系? 重要       
33 生存期内是常量,而对于整个类而言却是可变的。         
函数设计
序号 审查项目 重要性 结论 备注
34 参数的书写是否完整?不要贪图省事只写参数的类型而省略参数名字。         
35 参数命名、顺序是否合理?        参数命名逻辑性强可读性充分
36 参数的个数是否太多?         
37 是否使用类型和数目不确定的参数?         
38 是否省略了函数返回值的类型?         
39 函数名字与返回值类型在语义上是否冲突?         
40 是否将正常值和错误标志混在一起返回?正常值应当用输出参数获得,而错 重要       
41 在函数体的“入口处”,是否用assert对参数的有效性进行检查? 重要      并未声明检查函数
42 使用滥用了assert? 例如混淆非法情况与错误情况,后者是必然存在的并且 重要      未使用assert
43 return语句是否返回指向“栈内存”的“指针”或者“引用”? 重要       
44 是否使用const提高函数的健壮性?const可以强制保护函数的参数、返回值,         
内存管理
序号 审查项目 重要性 结论 备注
45 用malloc或new申请内存之后,是否立即检查指针值是否为NULL? 重要      未使用malloc
46 是否忘记为数组和动态内存赋初值? 重要      数组未赋初值,未使用动态内
47 数组或指针的下标是否越界? 重要       
48 动态内存的申请与释放是否配对? 重要 不适用 未使用动态内存
49 是否有效地处理了“内存耗尽”问题? 重要       
50 是否修改“指向常量的指针”的内容? 重要       
51 是否出现野指针?例如 重要   未出现野指针
1) 指针变量没有被初始化     
2) 用free或delete释放了内存之后,忘记将指针设置为NULL。       
52 是否将malloc/free 和 new/delete 混淆使用? 重要      使用free
53 malloc语句是否正确无误?例如字节数是否正确?类型转换是否正 确? 重要      未使用malloc语句
54 在创建与释放动态对象数组时,new/delete的语句是否正确无误? 重要      未使用动态对象数组
其它常见问题 
序号 审查项目 重要性 结论 备注
55 数据类型问题: 重要    
1) 变量的数据类型有错误吗?      没有错误
2) 存在不同数据类型的赋值吗?      不存在
3) 存在不同数据类型的比较吗?      不存在
56 变量值问题: 重要    
1) 变量的初始化或缺省值有错误吗?       
2) 变量发生上溢或下溢吗?      取决于输入的数字大小,一般
3) 变量的精度够吗?      精度不够
57 逻辑判断问题: 重要    
1) 由于精度原因导致比较无效吗?       
2) 表达式中的优先级有误吗?      没有错误
3) 逻辑判断结果颠倒吗?        
58 循环问题: 重要    
1) 循环终止条件不正确吗?      正确无误
2) 无法正常终止(死循环)吗?       
3) 错误地修改循环变量吗?       
4) 存在误差累积吗?      没有误差的积累
59 错误处理问题: 重要    
1) 忘记进行错误处理吗?      进行了充分的错误处理并且验证结果
2) 错误处理程序块一直没有机会被运行?      有机会被运行
3) 错误处理程序块本身就有毛病吗?      没有毛病
4) 错误处理程序块是“马后炮”吗?如在被它被调用之前软件已经出错。      并没有在之前出错
60 文件I/O问题: 重要   未对文件进行操作
1) 对不存在的或者错误的文件进行操作吗?     
2) 文件以不正确的方式打开吗?     
3) 文件结束判断不正确吗?     
4) 没有正确地关闭文件吗?     

4.代码评价

代码如下:

#include<bits/stdc++.h>

using namespace std;

int visited[100];

struct Arcnode
{
    int adjver;///编号
    Arcnode *nextarc;///下一条边
};

typedef struct Vnode
{
    char data;
    Arcnode *firstarc;///节点连的第一条边
}Vnode,Adjlist[100];

struct Graph
{
    Adjlist vertices;///顶点
    int vernum,arcnum;
    //int kind;
};

void Graphcreat(Graph &G)
{
    char a,b;
    Arcnode *q;
    scanf("%d",&G.vernum);
    for(int i=0;i<G.vernum;i++)
    {
        cin>>G.vertices[i].data;
        G.vertices[i].firstarc=NULL;
    }
    scanf("%d",&G.arcnum);
    for(int i=0;i<G.arcnum;i++)
    {
        int m,n;
        cin>>a>>b;
        for(int j=0;j<G.vernum;j++)
        {
            if(a==G.vertices[j].data)
                m=j;
            if(b==G.vertices[j].data)
                n=j;
        }
        q=(Arcnode *)malloc(sizeof(Arcnode));
        q->adjver=n;
        q->nextarc=G.vertices[m].firstarc;
        G.vertices[m].firstarc=q;
    }
}

void DFS(Graph &G,int v)
{
    Arcnode *p;
    visited[v]=1;
    cout<<G.vertices[v].data<<" ";
    p=G.vertices[v].firstarc;
    while(p!=NULL)
    {
        if(visited[p->adjver]!=1)
            DFS(G,p->adjver);
        p=p->nextarc;
    }
}

void BFS(Graph &G,int v)
{
    Arcnode *p;
    for(v=0;v<G.vernum;++v)
    {
        if(!visited[v])
        {
            visited[v]=1;
            cout<<G.vertices[v].data<<" ";
            p=G.vertices[v].firstarc;
            for(int w;p!=NULL;p=p->nextarc)
            {
                w=p->adjver;
                if(!visited[w])
                {
                    visited[w]=1;
                    cout<<G.vertices[w].data<<" ";
                }
            }
        }
    }
}

int main()
{
    Graph G;
    Graphcreat(G);
    DFS(G,0);
    cout<<endl;
    memset(visited,0,sizeof(visited));
    BFS(G,0);
    return 0;
}

通过对赵一霖同学的这个项目的代码的审查,可看出她使用C++语言进行开发,整体结构以及逻辑性很强,代码等具有可读性,格式具有一定的规范性,当然也有其他方面的不足(比如注释较少,文件的未使用,错误分析以及测试样例没有达到高层覆盖等)。
总体进行评价:
@规模较小(100行多一点)
@执行效率一般(执行时间比较短)
@占用空间较小 (占用系统内存较小)
@可读性较强(代码逻辑清晰,格式规范)
@扩展性很强(可以进行对图的改造)
其中分析主要优点如下:

1).对代码的整体结构有较为清晰的解读,声明了所使用的库函数的头文件,依此类推,各个函数之间模块化的定义,主函数调用的规范性。

2).语句与语句间使用换行明确区分,缩进得当,且大部分的循环体、判断结构均用“{”和“}”加以明示,代码可读性较强。

3).每一个函数内部清晰,语句通俗易懂,不同变量明确区分,不同语句之间的定义也可明确区分。

4).代码清晰简洁且通俗易懂,增加了代码的可读性。

运用了结构体和链表,增加代码的可扩展性,程序开始便定义了结构体,链表,以及数组的全局变量的使用,如下图所示:

int visited[100];

struct Arcnode
{
    int adjver;///编号
    Arcnode *nextarc;///下一条边
};

typedef struct Vnode
{
    char data;
    Arcnode *firstarc;///节点连的第一条边
}Vnode,Adjlist[100];

struct Graph
{
    Adjlist vertices;///顶点
    int vernum,arcnum;
    //int kind;
};

文件结构方面,逻辑较强,系统实现了对头文件的声明;
程序板式方面,空格运用恰当,长行拆分得体;
命名规则方面,命名规则与所采用的操作系统或开发工具的风格保持一致,标识符直观个别不可拼读,程序中出现了全部变量,函数名、变量和参数、常量的书写格式遵循一定的规则;
表达式与基本语句方面,代码行中的运算符比较多,用括号清楚地确定表达式的操作顺序,未编写太复杂或者多用途的复合表达式;
常量方面未设置
函数设计方面,合理且有序的参数传递及函数调用,参数的书写完整,不贪图省事只写参数的类型而省略参数名字。参数命名、顺序比较合理。提高函数的健壮性。函数名字与返回值类型在语义上不冲突;
内存管理方面,为数组和动态内存赋初值,但是没有数组或指针的下标是否越界。边界情况未考虑,而且没有有效解决内存耗尽的问题!但是没有出现野指针等问题;
其它常见问题,变量的精度不够,这是一个待解决的问题。循环没有存在误差累计,错误处理较少,测试样例较少。不过函数的定义以及作用基本正确;

待改进方面:
边界情况的考虑,数组越界的考虑,定义变量的可读性,精度进一步提升,测试样例的增加以及覆盖的程度!最最最主要的是注释!!!(我的代码好像也很少,因此需要共同提升!)

5.Summarize

代码复审可以从另一个角度发现缺陷和更好的解决办法,确保至少另外还有一人熟悉你的代码,促进知识共享。
代码审查要彻底!!!!

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄