通用链表 - 如何写出高质量代码
-
在项目开发的过程中,我们不可避免的遇到一些在编译前无法知道信息大小的场景,需要使用链表存储。比如解TS文件流时需要解出pat表,pmt表,sdt表,eit表等。但是每一种表所组成的链表结点信息都不一样,通用做法是每一个表都需要写对应的链表插入函数,打印函数和删除函数。这样会导致拥有大量的重复相似代码,为了提高链表函数的利用率,我们在下面引入通用链表的概念
-
所谓通用链表,其思想是利用void *万能指针实现,将数据域设置为指针,再调用时指向存放数据的结构体地址,从而达到不同类型结点使用同一套链表插入,打印,删除等操作。
-
代码示例
-
link_list.h
-
lin_list.h提供了三个链表函数,分别为添加链表结点,打印链表,销毁链表,其中打印链表用到回调函数。如果不是很了解回调函数可以看https://blog.youkuaiyun.com/MOSHIWANGJUE/article/details/105554956
/************************************************************************* * Filename : link_list.h * Description : Provide interfaces for external access * Version : 1.0 * History : * hehejun 2020-5-5 Create **************************************************************/ #ifndef _LINK_LIST_H_ #define _LINK_LIST_H_ typedef struct list { void *data; struct list *next; }GENAL_LIST; GENAL_LIST *append_link_list(GENAL_LIST *link_list, int data_size, void *add_node); void print_link_list(GENAL_LIST *link_list, void(*handle)(const void *)); void destory_link_list(GENAL_LIST *link_list); #endif
-
link_list.c
-
插入链表使用的是头插法,将需要传入值外部赋值后传入,通过data指针指向其地址,达到实现通用插入的效果
/************************************************************************* * Filename : link_list.c * Description : create general_link_list function * Version : 1.0 * History : * hehejun 2020-5-5 Create **************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "link_list.h" /************************************************************** * Function Name : append_link_list * Description : insert new node to link_list * Parameters : * link_list -- need link_list deal with * data_size -- need insert node data_size * add_node -- need insert node * Returns : link_list header pointer **************************************************************/ GENAL_LIST *append_link_list(GENAL_LIST *link_list, int data_size, void *add_node) { GENAL_LIST *new_node = NULL; if ((0 == data_size ) || (NULL == add_node)) //Parameter validity check { printf("input parameters illegal\n"); return NULL; } new_node = (GENAL_LIST *)malloc(data_size); //mallloc insert_node if (NULL == new_node) { printf("no enough memory to malloc\n"); } new_node->data = add_node; new_node->next = link_list; return new_node; } /************************************************************** * Function Name : print_link_list * Description : printf link_list node value one by one * Parameters : * link_list -- need link_list deal with * handle -- callback function's pointer * Returns : NULL **************************************************************/ void print_link_list(GENAL_LIST *link_list, void(*handle)(const void *)) { GENAL_LIST *current_node = link_list; if (NULL == link_list) //Parameter validity check { printf("link_list is NULL\n"); return ; } while(NULL != current_node) { handle(current_node->data); current_node = current_node->next; } } /************************************************************** * Function Name : destory_link_list * Description : free link_list node * Parameters : * link_list -- need link_list deal with * Returns : NULL **************************************************************/ void destory_link_list(GENAL_LIST *link_list) { GENAL_LIST *need_delete_node = NULL; while (NULL != link_list) { need_delete_node = link_list; link_list = link_list->next; free(need_delete_node); } }
-
main.c
-
分别定义两种结构体结点的链表,分别为pat表和个人信息表,使用通用链表的三个函数进行操作
#include <stdio.h> #include <stdlib.h> #include "link_list.h" typedef struct Node { unsigned int program_number; unsigned int program_pid; }DATA_NODE; typedef struct persion { char *name; unsigned int age; char *university; }PERSION; void print_node(const void *p); void print_persion(const void *p); int main(int arg, char *argv[]) { GENAL_LIST *pat_table = NULL; GENAL_LIST *persion_table = NULL; DATA_NODE node1; DATA_NODE node2; PERSION node3; PERSION node4; node1.program_number = 0x00; node1.program_pid = 0x12; node2.program_number = 0x01; node2.program_pid = 0x4e; node3.name = "zhangsan"; node3.age = 23; node3.university = "jiangxi university"; node4.name = "lisi"; node4.age = 22; node4.university = "jiangxi university"; pat_table = append_link_list(pat_table, sizeof(DATA_NODE), &node1); pat_table = append_link_list(pat_table, sizeof(DATA_NODE), &node2); print_link_list(pat_table, print_node); persion_table = append_link_list(persion_table, sizeof(PERSION), &node3); persion_table = append_link_list(persion_table, sizeof(PERSION), &node4); print_link_list(persion_table, print_persion); destory_link_list(pat_table); pat_table = NULL; destory_link_list(persion_table); persion_table = NULL; return 0; } void print_node(const void *p) { DATA_NODE *q = (DATA_NODE *)p; printf("program_number %x\n", q->program_number); printf("program_pid %x\n", q->program_pid); } void print_persion(const void *p) { PERSION *q = (persion *)p; printf("persion.name : %s\n", q->name); printf("persion.age : %d\n", q->age); printf("persion.university : %s\n", q->university); }
-
编译运行
-
-
总结:通用链表可以实现对结点结构不同的链表进行操作,达到省略重复相似代码的效果,提高代码质量,但同时由于使用void *万能指针,在程序控制方面需要细心。