實驗五:用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(),且是直接使用函數名不帶括號
回調函數既然作爲外層函數的參數傳遞給他,則在外層函數的函數體裏面 必然會用到該函數