【Jerryykt1464929958440 + 《軟件工程(C編碼實踐篇)》MOOC課程作業http://mooc.study.163.com/course/USTC-1000002006 】
github地址:https://github.com/JerryLittleBear/experiment.git
【實驗目的】:
1、給未修改找bug,quit命令無法運行的bug
2、利用callback函數參數使Linktable的查詢接口更加通用
3、隱藏接口信息
【實驗流程】:
1、找到quit命令無法運行的BUG
我通過將自建函數void say();寫進InitMenuData函數,此時say函數位於鏈表的最後一個結點。
int InitMenuData(tLinkTable ** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
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);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "say";
pNode->desc = "anything is ok";
pNode->handler = say;
AddLinkTableNode(*ppLinktable,(tLinkTableNode* )pNode);
return 0;
}
此時發現quit可以運行,而say無法運行了。
這就說明bug應該在各個鏈表操作函數中。很快在SearchLinkTableNode函數中發現,在遍歷鏈表的while循環中,while括號內的循環終止條件有問題:
while(pNode != pLinkTable->pTali)
這個邏輯是當鏈表遍歷到最後一個結點時(還未檢查最後一個結點的數據)就退出循環了,這樣對鏈表的最後一個結點就會直接跳過,所以鏈表存儲的最後一個函數必然無法被找到、被執行。在老師給的代碼中,quit函數就是最後一個鏈表結點,因此無法執行,我修改了InitMenuData後,say函數是最後一個鏈表結點,無法被執行。
要解決BUG,應修改循環條件爲
while(pNode != NULL)
這樣,最後一個鏈表結點的數據就能夠被遍歷,quit就能夠被SearchLinkTableNode函數找到,從而運行。
2、利用callback函數參數使Linktable的查詢接口更加通用
callback方式調用函數,簡單來說就是定義一個函數A,它的參數列表中帶有一個函數指針,這樣以來,我們可以通過調用函數A,然後將其他函數B作爲A的參數傳遞給A,這樣A執行時,可以獲得B的入口地址,從而在A執行過程中調用B。這種方式通常用於A、B函數不在同一個文件中,可以起到隱藏函數具體實現的作用。callback函數在本實驗中應用如下:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int (*Conditon)(tLinkTableNode * pNode, void* cmd), void* arg)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode, arg) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
其中,SearchLinkTableNode函數的第二個參數就是函數指針Conditon,本程序通過這個函數指針來指向其他函數的入口地址,能間接調用其他函數。我在實驗中發現兩個要點:
1.C語言中函數指針中有參數和沒參數都是可以的(但C++中函數指針的參數列表必須和它指向的函數一致)。
2.函數名即其入口地址,對函數名解引用依然是其入口地址,所以函數指針在使用時,解不解引用都可以,反正都是函數入口地址。
爲了讓接口更通用,我將用來接受命令行輸入的全局變量char cmd[CMD_MAX_LEN];移到main函數中。爲了做到這一點,必須修改多個函數的參數列表及其頭文件,如:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int (*Conditon)(tLinkTableNode * pNode, void* cmd), void* arg)
tDataNode* FindCmd(tLinkTable * head, char * cmd)
int SearchCondition(tLinkTableNode * pLinkTableNode, void * cmd)
在他們的參數中都加入了一個字符指針或空指針,這樣可以讓接口更具有通用性。
3、隱藏接口信息
爲達成該目標,需避免將鏈表數據結構的具體信息暴露給用戶,所以將結構體信息:
struct LinkTable
{
tLinkTableNode *pHead;
tLinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
放在linktable.c中,在頭文件中用一句typedef struct LinkTable tLinkTable;將必要信息告訴用戶。
【我學到了】:
1、做了以下小實驗:
#include <stdlib.h>
typedef struct time
{
int* second;
int minute;
}time;
int main()
{
time* tiPtr;
tiPtr = (time*)malloc(sizeof(time));
printf("有具體值:%p\n", tiPtr);
printf("有具體值:%d\n", tiPtr->minute);
putchar(10);
time* tiPtr1;
printf("輸出NULL:%p\n", tiPtr1);
printf("內存段錯誤,引用了未知地址:%d\n", tiPtr1->minute);
return 0;
}
這個實驗說明了:指向結構體的指針必須有具體指向的時候才能通過->來引用成員,否則會引用到未知內存段,產生段錯誤。
2、做了另一個小實驗:
#include <stdio.h>
int maxnum(int,int);
int main()
{
int (*funcPtr)();
funcPtr = maxnum;
printf("函數名代表函數入口地址:%p\n", funcPtr);
printf("函數指針解引用後還是其入口地址:%p\n", *funcPtr);
return 0;
}
int maxnum(int a, int b)
{
return a>b?a:b;
}
這個實驗說明了,函數入口地址即函數名,是一個地址,但是對他解引用後還是它本身。
3、對callback函數做了一些調研
深入淺出剖析函數指針和回調函數:http://blog.csdn.net/morixinguan/article/details/65494239
【關鍵代碼】:
代碼分爲3個文件:menu.c、linktable.c、linktable.h
源代碼如下:
linktable.c:
#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* cmd);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int (*Conditon)(tLinkTableNode * pNode, void* cmd), void* arg)
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode, arg) == 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;
}
menu.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"
int Help();
int Quit();
int say();
#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10
/* data struct and its operations */
typedef struct DataNode
{
tLinkTableNode * pNext;
char* cmd;
char* desc;
int (*handler)();
} tDataNode;
int SearchCondition(tLinkTableNode * pLinkTableNode, void * cmd)
{
tDataNode * pNode = (tDataNode *)pLinkTableNode;
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)
{
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;
}
int InitMenuData(tLinkTable ** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
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);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "say";
pNode->desc = "anything is ok";
pNode->handler = say;
AddLinkTableNode(*ppLinktable,(tLinkTableNode* )pNode);
return 0;
}
/* menu program */
tLinkTable * head = NULL;
int main()
{
InitMenuData(&head);
char cmd[CMD_MAX_LEN];
while(1)
{
printf("Input a cmd number > ");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
//printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
}
}
return 0;
}
int Help()
{
ShowAllCmd(head);
return 0;
}
int Quit()
{
exit(0);
}
int say()
{
printf("good day!\n");
return 0;
}
linktable.h:
#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);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int (*Conditon)(tLinkTableNode * pNode, void* arg), void* arg);
/*
* get LinkTableHead
*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
* get next LinkTableNode
*/
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
#endif /* _LINK_TABLE_H_ */
【實驗截圖】:
1、程序編譯運行,help嵌套調用findCmd顯示命令列表,quit函數退出程序,還有一些功能函數。
2、上傳github進行版本控制。