rn_xtcxyczjh-4 整理2[改良宏 重写双向链表接口 自动测试程序(assert)]

2015.09.25-09.27
读xtcxyczjh(系统程序员成长计划)—- 学习程序设计方法。

继“rp_xtcxyczjh-III 整理I[小公码(宏 错误包装) Makefile]”整理。本次整理内容包括:

  • 改写测试函数参数是否有效的宏函数。
  • 重写双向链表接口。
  • 编写自动测试双向链表接口的自动测试程序。
    整理后的代码保存地址:y15m9d25-27。

2015.09.25
需求简述。
手工测试比没有测试强一点,但是它存在的问题让它很难在实践中应用:手工输入数据的过程单调乏味,很难长期坚持。每次都要重新输入数据,浪费大量时 间。测试用例不能累积,测试往往不完整。用人脑判断输出的正误,浪费人力也存在误差。要写得好测试自然不能省,要写得快就需要更好的测试方法。

准备。
阅读作者书本相关部分及自动测试部分代码。

y15m9d22程序为每个函数编写了判定参数是否正确的宏(不够简洁),然而没有对传入正确或不正确参数时的情况进行测试,也没有对函数的功能进行验测试。根据作者介绍的自动测试,编写自动测试程序来测试程序功能,节约调试时间。

1. 改写测试函数参数是否有效的宏

看了作者判定参数合法的宏后觉得自己的宏写得很挫,也应该改成那样。将函数返回值作为参数传递给宏,可以减少不止一半的宏定义。

/* tk.h */
/* 定义宏以及声明tk.c中的内容 */

/* --------宏定义区域-------- */
#define PARA_INVALID_RETURN_NULL NULL
#define PARA_INVALIED_RETURN_VALUE -1
//如果参数无效则返回一个值ret
#define return_val_if_p_invalid(p, ret) if (!(p)) { \
                                                fprintf(stderr, "%s:%s:%d "#p" invalid\n", \
                                                        __FILE__, __func__, __LINE__);      \
                                                return ret;                                 \
                                            }

// 如果p参数不符合要求则返回
#define return_if_p_invalid(p) if (!(p)) { \
                                                fprintf(stderr, "%s:%s:%d "#p" invalid\n", \
                                                        __FILE__, __func__, __LINE__);      \
                                                return ;                                    \
                                            }

改良宏后替换原来的宏。

2015.09.26

2. 重写双向链表

检查双向链表的各个接口,重新编写双向链表的接口(重写的原因不变:跟作者的代码一比,显得有些搓)。这个工作应该在宏之前的,但由于菜的原因就在此时修改(多做一些重复工作罢了;这似乎要比原来就设计好再写程序要花更多的时间)。

(1) Makefile框架

根据先前的Makefile就可以直接在各个文件中添写内容了。
新建各文件。
main.c:包含C程序入口的文件。
dlist.c:双向链表数据结构及接口实现的文件。
dlist.h:声明dlist.c内容的文件。
tk.c:工具函数/错误包装函数实现。
tk.h:声明tk.c中的内容。
Makefile:包含编译各文件的命令。

为了只需make一下就能根据各文件生成可执行文件,先将各文件的内容准备如下。
main.c

/* main.c */
/* 包含C程序入口 */
#include "dlist.h"
#include "tk.h"

int main(void)
{
    return 0;
}

dlist.c

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
#include "dlist.h"
#include "tk.h"

dlist.c中将用dlist.h中的某些类型定义(或因函数定义的先后顺序而需要dlist.h中的函数声明);将用tk.h中的判定参数是否有效的宏等定义。

dlist.h

/* dlist.h */
/* 声明dlist.c中数据类型名及函数 */

tk.c

/* tk.c */
/* 定义工具函数以及其它函数的错误封包函数 */
#include "tk.h"
#include "dlist.h"

tk.c将用tk.h中宏或函数声明;tk.c可能会为dlist.h中所声明的函数进行错误封包。

tk.h

/* tk.h */
/* 定义宏以及声明tk.c中的内容 */

/* --------宏定义区域-------- */
#define PARA_INVALID_RETURN_NULL NULL
#define PARA_INVALIED_RETURN_VALUE -1
//如果参数无效则返回一个值ret
#define return_val_if_p_invalid(p, ret) if (!(p)) { \
                                                fprintf(stderr, "%s:%s:%d "#p" invalid\n", \
                                                        __FILE__, __func__, __LINE__);      \
                                                return ret;                                 \
                                            }

// 如果p参数不符合要求则返回
#define return_if_p_invalid(p) if (!(p)) { \
                                                fprintf(stderr, "%s:%s:%d "#p" invalid\n", \
                                                        __FILE__, __func__, __LINE__);      \
                                                return ;                                    \
                                            }

tk.h继承了原本的内容。

根据各个文件的关系(由之前得来)编写Makefile

#Makefile
#make命令默认执行make all或者第一条规则
all:        main.o dlist.o tk.o
    $(CC) $^ $(CFLAGS) $@

main.o:     main.c dlist.h tk.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

dlist.o:    dlist.c dlist.h tk.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

tk.o:       tk.c dlist.h tk.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)
clean:
    -rm *.o

#定义变量
CC=gcc
CFLAGS=-o
CFLAG_OBJ=-c
CFLAGS_POSTFIX=-Wall

在linux终端使用make命令,成功生成了可执行文件all。Makefile的准备工作已经做好,下面来实现具体的内容。

(2) 描述双向链表的数据结构

描述双向链表的数据结构保持不变:一个描述双向链表节点的结构体,一个管理循环双向链表的结构体(一个指向循环双向链表节点的首节点的指针,一个记录循环双向链表的个数)。不过发现命名含义不够明确,改一下命名。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */

/* --------类型定义区-------- */
//描述双向链表节点的结构体
typedef struct _DlistNode {
    void    *data;
    struct  _DlistNode *prev;
    struct  _DlistNode *next;
}DlistNodeT;

//管理双向链表的结构体
struct _DlistManage {
    DlistNodeT  *first;
    unsigned int num;
};

将描述/管理双向链表的结构体定义在dlist.c中,对外声明其类型即可隐蔽描述/管理双向链表的数据结构。通过将struct _DlistManage类型名声明给其它文件(调用者),结合这个声明设计接口来让调用者对双向链表进行基本的操作(对于双向链表的接口,若不给其它文件调用则在定义时将其限制为static属性)。

在dlist.h(方便其它文件包含)中声明管理双向链表的类型的名字。

/* dlist.h */
/* 声明dlist.c中数据类型名及函数 */

/* --------数据类型声明区--------*/
struct _DlistManage; //不完全类型声明
typedef struct _DlistManage DlistManageT;

(3) 双向链表的接口

分配双向链表的一个节点。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
#include "dlist.h"
#include "tk.h"
#include <stdlib.h>
#include <stdio.h>
//……
/* -------函数定义区-------- */
//创建双向链表的一个节点的子函数
static DlistNodeT *create_dlist_node(void *data)
{
    DlistNodeT  *pnd = NULL;

    return_val_if_p_invalid(data, NULL);
    pnd = (DlistNodeT *)malloc( sizeof(DlistNodeT) );
    if (NULL != pnd) {
        pnd->data   = data;
        pnd->prev   = NULL;
        pnd->next   = NULL;
    }
    return pnd;
}

创建双向链表的一个节点是创建n个节点的双向链表、向双向链表中插入节点等功能的子功能,它被应用于dlist.c的内部,其它文件不得调用,故而将其限制为static属性

创建管理双向链表的结构体指针。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...
//创建管理双向链表的结构
static DlistManageT *Dlicreate_dlist_manage(void)
{
    DlistManageT    *pdl = NULL;
    pdl = (DlistManageT *)malloc( sizeof(DlistManageT) );
    if (NULL != pdl) {
        pdl->first  = NULL;
        pdl->num    = 0;
    }
    return pdl;
}

此函数主要是被创建包含n个节点的的双向链表调用。

创建n个节点的双向链表。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...
//创建含n个节点的双向链表
DlistManageT *create_dlist(unsigned int len)
{
    unsigned int    i = 0;
    DlistNodeT      *pndc = NULL;
    DlistNodeT      *pndl = NULL;
    DlistManageT    *pdl = NULL;

    //判断参数len是否为大于0的正整数
    return_value_if_p_invalid(len > 0, NULL);

    //创建管理双向链表的结构体指针
    if (NULL == (pdl = create_dlist_manage()))
        return NULL;

    //双向链表中的第一个节点
    if (NULL == (pndc = create_dlist_node(NULL))) {
        free_dlist(pdl);
        return NULL;
    }
    pdl->first  = pndc;
    pdl->mum++;

    //双向链表中的后续节点
    for (i = 1; i < len; ++i) {
        pndl    = pndc;
        if (NULL == (pndc = create_dlist_node(NULL))){
            free_dlist(pdl);
            return NULL;
        }
        pndl->next  = pndc;
        pndc->prev  = pndl;
        pdl->num++;
    }
    pdl->first->prev    = pndc;
    pndc->next          = pdl->first;

    return pdl;
}

创建双向链表中的free_dlist为释放双向链表函数,其定义如下。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...
//通过管理双向链表指针释放双向链表及管理双向链表的结构体指针
void free_dlist(DlistManageT *pdl)
{
    unsigned int i = 0;
    unsigned int len = 0;
    DlistNodeT  pndc = NULL;
    DlistNodeT  pndn = NULL;

    //判断参数是否不为NULL
    return_if_p_invalid(pdl);

    //释放双向链表中的节点
    len     = pdl->num;
    pndc    = pdl->first;
    for (i = 0; i < len; ++i) {
        pndn    = pndc->next;
        pndc->prev  = NULL;
        pndc->next  = NULL;
        pndc->data  = NULL;
        free(pndc);

        pdl->num--;
        pndc    = pndn;
    }

    //释放管理双向链表的结构体指针
    pdl->first = NULL;
    free(pdl);
}

初始化链表。
(循环)双向链表中的每一个节点可以指向任何数据类型的数据(双向链表被创建后,每个节点指向的内容都为NULL),也只有调用者才知道要往双向链表中存储什么类型的数据。故而把初始化链表的任务交给调用者。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//返回双向链表中第i个节点
static DilistNodeT *get_dlist_any_node(DlistManageT *pdl, unsigned i)
{
    unsigned int k = 0;
    unsigned int len = 0;
    DlistNodeT  *pnd = NULL;

    //判断参数是否无效
    return_val_if_p_invalid(pdl, NULL);
    len = pdl->num;
    return_val_if_p_invalid(i > 0 && i <= len, NULL);

    //查找第i个节点
    pnd = pdl->first;
    for (k = 1; k < i; ++k)
        pnd = pnd->next;

    return pnd;
}

//给双向链表中第i个节点中指向数据(即data)的指针赋值
int init_dlist_any_node_data(DlistManageT *pdl, unsigned int i, void *data)
{
    unsigned int len = 0;
    DlistNodeT  *pnd = NULL;

    //判断参数是否无效
    return_val_if_p_invalid(pdl && data, PARA_INVALIED_RETURN_VALUE);
    len = pdl->num;
    return_val_if_p_invalid(i > 0 && i <= len, PARA_INVALIED_RETURN_VALUE);

    if (NULL != (pnd = get_dlist_any_node(pdl, i))) {
        pnd->data   = data;
        return 0;
    }

    return -1;
}

不用给调用者直接访问到双向链表中的元素机会,将调用者要赋予的值拿到接口内实现赋值操作。这里给调用者提供了给双向链表节点赋值这个接口。

插入/删除双向链表中的任意节点。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//向双向链表中第i个位置插入一个节点,第i个位置指第i个节点之前, i = len + 1时表示插入到最后一个节点之后
int insert_dlist_node(DlistManageT *pdl, unsigned int i, void *data)
{
    unsigned int k = 0;
    unsigned int len = 0;
    DlistNodeT  *pndi = NULL;
    DlistNodeT  *pndc = NULL;

    //判断参数是否无效
    return_val_if_p_invalid(pdl, PARA_INVALIED_RETURN_VALUE);
    len = pdl->num;
    return_val_if_p_invalid(len != 0 && i > 0 && i <= len + 1, PARA_INVALIED_RETURN_VALUE);

    //创建一个节点
    pndi    = create_dlist_node(data);
    return_val_if_p_invalid(pndi, PARA_INVALIED_RETURN_VALUE);

    //寻找插入双向链表中某节点之前的节点
    pndc    = pdl->first;
    for (k = 1; k < i; ++k)
        pndc    = pndc->next;

    //插入节点
    pndi->prev          = pndc->prev;
    pndi->next          = pndc;
    pndc->prev->next    = pndi;
    pndc->prev          = pndi;
    if (1 == k)
        pdl->first = pndi;

    return 0;
}

//删除双向链表中的第i个节点
int delete_dlist_node(DlistManageT *pdl, unsigned int i)
{
    unsigned int k = 0;
    unsigned int len = 0;
    DlistNodeT  *pnd = NULL;

    //判断参数是否有效
    return_val_if_p_invalid(pdl, PARA_INVALIED_RETURN_VALUE);
    len = pdl->num;
    return_val_if_p_invalid(i > 0 && i <= len, PARA_INVALIED_RETURN_VALUE);

    //查找删除的节点
    pnd = pdl->first;
    for (k = 1; k < i; ++k)
        pnd = pnd->next;

    //删除节点
    pnd->prev->next = pnd->next;
    pnd->next->prev = pnd->prev;
    pnd->prev       = NULL;
    pnd->next       = NULL;
    pnd->data       = NULL;
    free(pnd);

    return 0;
}

获取双向链表长度。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//获取双向链表的长度
unsigned int get_dlist_length(DlistManageT *pdl)
{
    return_val_if_p_invalid(pdl,PARA_INVALIED_RETURN_VALUE);
    return pdl->num;
}

获取第i个节点的值。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//获取双向链表中第i个节点的值
void *get_dlist_node_data(DlistManageT *pdl, unsigned int i)
{
    unsigned int k = 0;
    unsigned int len = 0;
    DlistNodeT  *pnd = NULL;

    return_val_if_p_invalid(pdl, NULL);
    len = pdl->num;
    return_val_if_p_invalid(i > 0 && i <= len, NULL);

    pnd = pdl->first;
    for (k = 1; k < i; ++k)
        pnd = pnd->next;

    return pnd->data;
}

遍历双向链表提供回调函数参数,让回调函数处理双向链表元素

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//遍历双向链表元素,调用回调函数对双向链表元素进行操作
int dlist_foreach(DlistManageT *pdl, pForeachDlistDataFunT pvt_fun, void *ctx)
{
    unsigned int    i = 0;
    unsigned int    len = 0;
    DlistNodeT      *pnd = NULL;

    //判断参数是否合理
    return_val_if_p_invalid(pdl & pvt_fun, PARA_INVALIED_RETURN_VALUE);

    //遍历双向链表中的元素
    len = pdl->num;
    pnd = pdl->first;
    for (i = 0; i < len; ++i) {
        pvt_fun(ctx, pnd->pe);
        pnd = pnd->next;
    }

    return 0;
}

关于双向链表的接口暂时就写这么多(接口的性能也暂时让它这么地)。在dlist.h中定义既要在dlist.c中用到又要在其它文件中用到的类型,声明定义在dlist.c中的接口。

/* dlist.h */
/* 声明dlist.c中数据类型名及函数 */

/* --------数据类型声明区--------*/
struct _DlistManage; //不完全类型声明
typedef struct _DlistManage DlistManageT;
typedef int (*pForeachDlistDataFunT)(void *ctx, void *data);

/* --------函数声明区-------- */
DlistManageT *create_dlist(unsigned int len);
void free_dlist(DlistManageT *pdl);
int init_dlist_any_node_data(DlistManageT *pdl, unsigned int i, void *data);
int insert_dlist_node(DlistManageT *pdl, unsigned int i, void *data);
int delete_dlist_node(DlistManageT *pdl, unsigned int i);
unsigned int get_dlist_length(DlistManageT *pdl);
void *get_dlist_node_data(DlistManageT *pdl, unsigned int i);

int dlist_foreach(DlistManageT *pdl, pForeachDlistDataFunT pvt_fun, void *ctx);

3. 自动测试程序(assert)

(1) autotest-I

本来应该是写一个测试一个的,这里就一起测了吧,以后就可以写一个测一个了。将测试双向链表接口的函数写在autotest.c内。在linux终端新建autotest.c和autotest.h文件。

/* autotest.c */
/* 自动测试程序: 用于测试dlist.c中所编写的接口 */
#include <assert.h>
#include <stdio.h>
#include "dlist.h"

/* --------函数定义区------- */
//用正确的参数测试dlist.c中的接口
void test_dlist_interface_with_right_para(unsigned int len)
{
    unsigned int    i = 0;
    unsigned int    ul = 0;
    int re = 0;
    int count = 0, newlen = 0;
    void *pdata = NULL;
    DlistManageT    *pdl = NULL;
    int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int dt[100];

    for (i = 0; i < 100; ++i)
        dt[i]   = data[i % 10];

    assert(NULL != (pdl = create_dlist(len)));
    assert(len == (ul = get_dlist_length(pdl)));
    for (i = 1; i <= len; ++i) {
        if (-1 != (re = init_dlist_any_node_data(pdl, i, &dt[(i - 1) % 100])))
            assert(dt[(i - 1) % 100] == *((int *)(pdata = get_dlist_node_data(pdl, i))));
    }


    for (i = 1; i < len; i += 2) {
        if (-1 != (re = insert_dlist_node(pdl, i, NULL))) {
            count++;
            assert(len + count == (ul = get_dlist_length(pdl)));
        }
    }
    newlen  = get_dlist_length(pdl);
    if (-1 != (re = insert_dlist_node(pdl, newlen + 1, NULL))) {
        assert(newlen + 1 == (ul = get_dlist_length(pdl)));
    }


    count   = 0;
    newlen  = get_dlist_length(pdl);
    for (i = 1; i < newlen - count; ++i) {
        if (-1 != (re = delete_dlist_node(pdl, i))) {
            count++;
            assert(newlen - count == (ul = get_dlist_length(pdl)));
        }
    }
    free_dlist(pdl);
}

在autotest.h中声明定义在autotest.c中的函数。

/* autotest.h */
/* 什么autotest.c中的函数 */

void test_dlist_interface_with_right_para(unsigned int len);

在main.c中包含autotest.h测试dlist.h中的接口。

/* main.c */
/* 包含C程序入口 */
#include "dlist.h"
#include "tk.h"
#include "autotest.h"

int main(void)
{
    test_dlist_interface_with_right_para(10);
    return 0;
}

修改Makefile。

#Makefile
#make命令默认执行make all或者第一条规则
all:        main.o dlist.o tk.o autotest.o
    $(CC) $^ $(CFLAGS) $@

main.o:     main.c dlist.h tk.h autotest.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

dlist.o:    dlist.c dlist.h tk.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

tk.o:       tk.c dlist.h tk.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

autotest.o: autotest.c dlist.h
    $(CC) $(CFLAG_OBJ) $< $(CFLAGS_POSTFIX)

clean:
    -rm *.o

#定义变量
CC=gcc
CFLAGS=-o
CFLAG_OBJ=-c
CFLAGS_POSTFIX=-Wall

在linux终端执行make命令后,运行./all发现以下运行错误:“all: autotest.c:35: test_dlist_interface_with_right_para: Assertion `len - count == (ul = get_dlist_length(pdl))’ failed. Aborted (core dumped)”这说明get_dlist_length(pdl)出了问题,到dlist.c中检查get_dlist_length(pdl)函数,发现删掉节点后没有对应用技术pdl->num–;在get_dlist_length()函数中补上pdl->num–;这条语句。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//…...

//删除双向链表中的第i个节点
int delete_dlist_node(DlistManageT *pdl, unsigned int i)
{
   //…...
    //删除节点
    pnd->prev->next = pnd->next;
    pnd->next->prev = pnd->prev;
    pnd->prev       = NULL;
    pnd->next       = NULL;
    pnd->data       = NULL;
    pdl->num--;//补充语句
    free(pnd);

    return 0;
}

修补delete_dlist_node这个函数后,再在linux终端运行make命令,然后运行./all得到以下错误:“Segmentation fault (core dumped)”。欧欧,常见的内存错误出现了。

通过屏蔽输出的方式找到问题依旧除在delete_dlist_node函数中,出现内存错误的原因在于“删除双向链表第一个节点后,管理双向链表的指针并未随之指向双向链表的下一个节点作为首节点”。如此,当再通过管理双向链表的指针访问双向链表时就犯了“使用已释放的指针”的内存错误。修改这个BUG后的代码如下。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//……

//删除双向链表中的第i个节点
int delete_dlist_node(DlistManageT *pdl, unsigned int i)
{
    //…...
    //查找删除的节点
    pnd = pdl->first;
    for (k = 1; k < i; ++k)
        pnd = pnd->next;

    if (1 == k) {    //代码修改 处...
        pdl->first  = pnd->next;
    }
    //删除节点
    //…...
    return 0;
}

同样,也找出了在insert_dlist_node()函数中的一个BUG:当插入一个节点后,pdl->num++语句忘记了写。insert_dlist_node()函数修改如下。

/* dlist.c */
/* 定义描述/管理(循环)双向链表的数据,定义(循环)双向链表的接口给 */
//……

int insert_dlist_node(DlistManageT *pdl, unsigned int i, void *data)
{
    //…...
    pdl->num++;
    if (1 == k)
        pdl->first = pndi;

    return 0;
}

在linux中断重新执行make命令,再运行./all程序。通过此次测试程序(虽然测试程序没有经过太多的大脑,不过还是算对自动测试程序有了一次体验)。

2015.9.27

(2) autotest-II

今天再来编写测试函数。也就是重写autotest.c。

/* autotest.c */
/* 自动测试程序: 用于测试dlist.c中所编写的接口 */
#include <assert.h>
#include <stdio.h>
#include "dlist.h"

/* --------函数定义区------- */
//用正确的参数测试dlist.c中的接口
void init_dlist(DlistManageT **pdl, unsigned int len)
{
    unsigned int i = 0;
    unsigned int ul = 0;
    int re = 0;
    void *pdata = NULL;
    int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int dt[100];

    for (i = 0; i < 100; ++i)
        dt[i]   = data[i % 10];

    assert(NULL != (*pdl = create_dlist(len)));
    assert(len == (ul = get_dlist_length(*pdl)));

    for (i = 1; i <= len; ++i) {
        assert(-1 != (re = init_dlist_any_node_data(*pdl, i, &dt[(i - 1) % 100])));
        assert(dt[(i - 1) % 100] == *((int *)(pdata = get_dlist_node_data(*pdl, i))));
    }
}

//用参数测试双向链表中的接口
void test_dlist_interface_with_para(DlistManageT **pdl)
{
    unsigned int i = 0;
    unsigned int count = 0;
    unsigned int len = 0;
    unsigned int nlen = 0;
    unsigned int ul = 0;
    int re = 0;

    len = get_dlist_length(*pdl);
    for (i = 1; i < len; i += 2) {
        assert(-1 != (re = insert_dlist_node(*pdl, i, NULL)));
        count++;
        assert(len + count == (ul = get_dlist_length(*pdl)));
    }
    nlen  = get_dlist_length(*pdl);
    assert(-1 != (re = insert_dlist_node(*pdl, nlen + 1, NULL)));
    assert(nlen + 1 == (ul = get_dlist_length(*pdl)));


    count   = 0;
    nlen  = get_dlist_length(*pdl);
    for (i = 1; i < nlen - count; ++i) {
        assert(-1 != (re = delete_dlist_node(*pdl, i)));
        count++;
        assert(nlen - count == (ul = get_dlist_length(*pdl)));
    }
}

//自动测试双向链表接口程序
void autotest_dlist_interface(void)
{
    unsigned int i = 0;
    unsigned int len = 10;
    DlistManageT *pdl = NULL;
    char *msg = "-------------";

    //i = 0的情况
    for (i = 1; i <= len; ++i) {
        init_dlist(&pdl, i);
        test_dlist_interface_with_para(&pdl);
        free_dlist(pdl);
        fprintf(stderr, "%d:%s\n", i, msg);
    }
}

在autotest.h中更新声明autotest.c中的函数。

/* autotest.h */
/* 什么autotest.c中的函数 */

void init_dlist(DlistManageT **pdl, unsigned int len);
void test_dlist_interface_with_para(DlistManageT **pdl);
void autotest_dlist_interface(void);

在main.c的主程序中调用测试函数。

/* main.c */
/* 包含C程序入口 */
#include "dlist.h"
#include "tk.h"
#include "autotest.h"

int main(void)
{
    autotest_dlist_interface();
    return 0;
}

在linux终端使用make命令编译程序,再运行./all程序。会有程序被终端的提示,将autotest_dlist_intface(void)程序中将i的初始值改为从1开始再次编译运行。程序顺利运行完毕。(程序中并未有太多测试参数无效的情况,作为一次体验就不去纠结测试细节了)

最后,将字符串小写转大写的函数搬运到tk.c中,声明在tk.h

/* tk.c */
/* 定义工具函数以及其它函数的错误封包函数 */
#include "tk.h"
#include "dlist.h"
#include <ctype.h>

/* --------函数定义区-------- */
//将小写字母转换为大写字母
char  lchar2uchar(unsigned char ch)
{
    if (islower(ch))
        return toupper(ch);
    else if (isupper(ch))
        return ch;
    else return -1;
}

/* tk.h */
/* 定义宏以及声明tk.c中的内容 */
//……
/* --------函数声明区-------- */
char  lchar2uchar(unsigned char ch);

在linux终端make,编译通过。

至于错误封包,那是调用时(者)的事情,要在main程序中调用这些接口时再写。

读《xtcxyczjh》-Part-IV pnote over.
[2015.09.27-14:21]

你可能感兴趣的:(c,程序设计方法)