实验五:用callback增强链表模块来实现命令行菜单小程序V2.8
实验要求:
1.给lab5-1.tar.gz找bug,quit命令无法运行的bug
2.利用callback函数参数使Linktable的查询接口更加通用
3.注意接口的信息隐藏
实验内容:
1、找bug
在自己的windows下 gcc 编译发现 多线程头文件不存在,了解知道windows下不存在这个文件 得自己安装,为了省事使用虚拟机 Ubuntu。
在linux下运行 发现下面这个警告,查看发现 strcmp()这个函数是在string.h文件中的 于是在menu.c开始地方#include<string.h>
修改后再编译通过 运行 发现quit不能退出
调试之后 发现一个错误存在 于 linktable.c中的SearchLinkTableNode函数 将while(pNode != pLinkTable -> pTail)改为while(pNode != NULL)之后重新编译再运行就正常了
2 利用callback函数参数使Linktable的查询接口更加通用以及注意接口的信息隐藏
menu.c
/**************************************************************/
/*Copyright (C) Je-vie.com, SSE@USTC, 2017-Now */
/* */
/* FILE NAME : menu.c */
/* AUTHOR : liujinfu */
/* MODULE NAME : menu */
/* LANGUAGE : C */
/* DATE OF FIRST RELEASE : 2017/10/21 */
/* DESCRIPTION : this is a menu program */
/**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "linktable.h"
#include <string.h>
/*声明两个函数*/
int Help();
int Quit();
/*宏定义 */
#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
/* 数据结构定义 用来保存每个命令的各种信息 即命令的信息节点 */
typedef struct DataNode
{
tLinkTableNode * pNext;
char* cmd;
char* desc;
int (*handler)();
} tDataNode;
/* */
int SearchCondition(tLinkTableNode * pLinkTableNode, void* args)
{
tDataNode * pNode = (tDataNode *)pLinkTableNode;
char* cmd = (char*)args;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
/*
这里就是这个实验的重点内容使用回调函数:
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,
然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,
由别人(或系统)的函数在运行时来调用的函数。
C语言中回调函数主要通过函数指针的方式实现。
这里的回调函数是SearchCondition(),且是直接使用函数名不带括号
回调函数既然作为外层函数的参数传递给他,则在外层函数的函数体里面必然会用到该函数
下面这个函数有三个参数,第二个是一个回调函数的指针
*/
return (tDataNode*)SearchLinkTableNode(head,SearchCondition,(void*)cmd);
}
/* show all cmd in listlist */
int ShowAllCmd(tLinkTable * head)
{
tDataNode * pNode = (tDataNode*)GetLinkTableHead(head);
while(pNode != NULL)
{
printf("%s - %s\n", pNode->cmd, pNode->desc);
pNode = (tDataNode*)GetNextLinkTableNode(head,(tLinkTableNode *)pNode);
}
return 0;
}
/* 这种方法还是比较low的*/
int InitMenuData(tLinkTable ** ppLinktable)
{
/*
初始化一个链表,获得一个存储该链表的信息的结构体变量的指针
该链表的信息节点包含了指向该链表的头节点和尾节点的指针以及链表长度
而头尾节点的指针指向的又是一个结构体定义的节点,该节点只有一个指针变量,
用于指向同结构体定义的下一个节点变量的指针,有点绕 这样的设计只是为了达到封装的效果
*/
*ppLinktable = CreateLinkTable();
/* 获得一个命令信息节点的指针 且初始化对应的节点里的变量*/
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
/*
将该信息节点链接到之前初始化的那个链表上,这里用到了结构体强制转换
这种设计要注意一些问题不然容易出错。详细原因参考另一篇博客:
http://blog.csdn.net/liu896749150/article/details/78302984
*/
AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "version";
pNode->desc = "Menu Program V1.0";
pNode->handler = NULL;
AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "quit";
pNode->desc = "Quit from Menu Program V1.0";
pNode->handler = Quit;
AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
return 0;
}
/* menu program */
tLinkTable * head = NULL;
int main()
{
/*初始化每个命令的对应信息,这些信息使用一个结构体数据类型保存*/
InitMenuData(&head);
/* cmd line begins */
while(1)
{
char cmd[CMD_MAX_LEN];
printf("Input a cmd number > ");
/* 获取命令行输入的字符串信息*/
scanf("%s", cmd);
/*
调用FindCmd()函数来进行统一的命令查找功能,找到了则会返回对应的
命令的信息节点的指针
*/
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
/*没找到 说明输入有误*/
printf("This is a wrong cmd!\n ");
continue;
}
/*
成功找到则 输出对应的信息(如果该节点有对应的方法函数的指针,
也可以调用函数方法进行运算操作,而不仅仅是输出固定的信息)
*/
printf("%s - %s\n", p->cmd, p->desc);
/*
这一步就是调用该节点的结构体中有一个指向该命令的
方法函数的指针handler(),然后运行该方法,也可以传参数
*/
if(p->handler != NULL)
{
p->handler();
}
}
}
int Help()
{
ShowAllCmd(head);
return 0;
}
int Quit()
{
exit(0);
}
linktable.h
/********************************************************************/
/* Copyright (C) SSE-USTC, 2012-2013 */
/* */
/* FILE NAME : linktabe.h */
/* PRINCIPAL AUTHOR : Mengning */
/* SUBSYSTEM NAME : LinkTable */
/* MODULE NAME : LinkTable */
/* LANGUAGE : C */
/* TARGET ENVIRONMENT : ANY */
/* DATE OF FIRST RELEASE : 2012/12/30 */
/* DESCRIPTION : interface of Link Table */
/********************************************************************/
/*
* Revision log:
*
* Created by Mengning,2012/12/30
*
*/
#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_
#include <pthread.h>
#define SUCCESS 0
#define FAILURE (-1)
/*
* LinkTable Node Type
*/
typedef struct LinkTableNode
{
struct LinkTableNode * pNext;
}tLinkTableNode;
/*
* LinkTable Type
*/
typedef struct LinkTable tLinkTable;
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable();
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode, void* agrs);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void* agrs), void* agrs);
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
#endif /* _LINK_TABLE_H_ */
linktable.c
/********************************************************************/
/* Copyright (C) SSE-USTC, 2012-2013 */
/* */
/* FILE NAME : linktabe.c */
/* PRINCIPAL AUTHOR : Mengning */
/* SUBSYSTEM NAME : LinkTable */
/* MODULE NAME : LinkTable */
/* LANGUAGE : C */
/* TARGET ENVIRONMENT : ANY */
/* DATE OF FIRST RELEASE : 2012/12/30 */
/* DESCRIPTION : interface of Link Table */
/********************************************************************/
/*
* Revision log:
*
* Created by Mengning,2012/12/30
* Provide right Callback interface by Mengning,2012/09/17
*
*/
#include<stdio.h>
#include<stdlib.h>
#include"linktable.h"
struct LinkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
/*
* Create a LinkTable
*/
tLinkTable * CreateLinkTable()
{
tLinkTable * pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable));
if(pLinkTable == NULL)
{
return NULL;
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_init(&(pLinkTable->mutex), NULL);
return pLinkTable;
}
/*
* Delete a LinkTable
*/
int DeleteLinkTable(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return FAILURE;
}
while(pLinkTable->pHead != NULL)
{
tLinkTableNode * p = pLinkTable->pHead;
pthread_mutex_lock(&(pLinkTable->mutex));
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
free(p);
}
pLinkTable->pHead = NULL;
pLinkTable->pTail = NULL;
pLinkTable->SumOfNode = 0;
pthread_mutex_destroy(&(pLinkTable->mutex));
free(pLinkTable);
return SUCCESS;
}
/*
* Add a LinkTableNode to LinkTable
*/
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pNode->pNext = NULL;
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == NULL)
{
pLinkTable->pHead = pNode;
}
if(pLinkTable->pTail == NULL)
{
pLinkTable->pTail = pNode;
}
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
/*
* Delete a LinkTableNode from LinkTable
*/
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return FAILURE;
}
pthread_mutex_lock(&(pLinkTable->mutex));
if(pLinkTable->pHead == pNode)
{
pLinkTable->pHead = pLinkTable->pHead->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode->pNext == pNode)
{
pTempNode->pNext = pTempNode->pNext->pNext;
pLinkTable->SumOfNode -= 1 ;
if(pLinkTable->SumOfNode == 0)
{
pLinkTable->pTail = NULL;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
pTempNode = pTempNode->pNext;
}
pthread_mutex_unlock(&(pLinkTable->mutex));
return FAILURE;
}
/*
* 该函数的参数中使用了回调函数
*Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode, void* agrs);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void* agrs), void* agrs)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
// while(pNode != pLinkTable->pTail)
while(pNode)
{
if(Conditon(pNode, agrs) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable)
{
if(pLinkTable == NULL)
{
return NULL;
}
return pLinkTable->pHead;
}
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
if(pLinkTable == NULL || pNode == NULL)
{
return NULL;
}
tLinkTableNode * pTempNode = pLinkTable->pHead;
while(pTempNode != NULL)
{
if(pTempNode == pNode)
{
return pTempNode->pNext;
}
pTempNode = pTempNode->pNext;
}
return NULL;
}
编译运行结果测试:
实验总结:
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,
然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,
由别人(或系统)的函数在运行时来调用的函数。
C语言中回调函数主要通过函数指针的方式实现。
这里的回调函数是SearchCondition(),且是直接使用函数名不带括号
回调函数既然作为外层函数的参数传递给他,则在外层函数的函数体里面 必然会用到该函数