如何实现C语言通用链表
C语言通用链表实现
C语言的数组有时候很好用,但在不知道数据有多少的时候,并且涉及到某个数据的删除时,操作起来很不方便,这个时候就需要用到链表。自己定义一个链表数据结构,然后实现它,此时这个链表仅能表示你要处理的相关数据,并不是通用的,这将导致你要使用的时候就得重新写数据结构的代码。这个时候,定义一个通用链表,用这个链表就能处理所有的数据类型了,因为是通用的,所以会有一部分代码需要在用户使用的时候自己完成。
常见链表定义
struct Student {
char name[20];
int id;
};
typedef struct node_t Node;
struct node_t {
struct Student stu;
Node *next;
};
链表只是一个抽象概念,这里定义Node节点就可以表示一个链表信息。
也可以在定义的标准一点:
typedef struct {
Node *head;
Node *tail;
size_t size;
} List;
这里定义的List就是一个完完全全的链表了,有头结点,尾节点,还有链表大小。当然还可以在这里面增加很多内容,比如定义双向链表等。但是这个链表只能处理学生信息,不具备通用性。
下面定义一个通用链表:
typedef struct {
void *data;
Node *next;
} Node;
typedef struct {
Node *head;
Node *tail;
size_t size;
} List;
通过void *
通用指针,我们可以处理不同的数据类型。(要是C语言可以用类型参数就好了)
通过这个通用链表,我们可以处理任何类型的链表。不在受到数据类型的约束。但是这会引入一个问题,就是关于data部分的内容,只能交给用户来进行处理 ,因为我们在写这个代码的时候并不清楚用户要使用何种类型的数据,只有在用户使用的时候才知道用什么数据。
所以链表该怎么写还是怎么写,当需要对data部分进行处理的时候,通过一个函数指针,把这部分的内容交给用户处理。这样,抽象出来的链表就是通用的,我们完成通用的部分,不通用的部分交给用户,用户在使用时,自己进行处理。这样能极大的减少代码量,还是能很方便的使用的。
list.h,list.c是我们定义的通用链表内容。用户使用的时候定义list_custom.h和list_custom.c,在这里完成data的定义和处理。
下面展示代码:
list.h
#ifndef __LIST_H__
#define __LIST_H__
#include <stdio.h>
typedef void (* p_free)(void *data);
typedef void *(* p_malloc)(size_t size);
typedef int (* p_traverse)(void *nodeData, void *data);
typedef struct note_t Node;
struct note_t{
void *data;
Node *next;
};
typedef struct {
Node * head;
Node * tail;
int size;
} List;
List * init_list(void);
int delete_list(List *plist, p_free pfun_free);
Node * create_node(void *data, p_malloc pfun_malloc, size_t size);
int delete_node(Node *pnode, p_free pfun_free);
int add_node(List *plist, Node *pnode);
int remove_node(List *plist, p_free pfun_free);
int remove_node_by_data(List *plist, void *data, p_traverse pfun_compare, p_free pfun_free);
int traverse_list(List *plist, void *data, p_traverse p_fun);
Node * get_node_by_index(List *plist, int index);
Node * find_node_by_data(List *plist, void *data, p_traverse pfun_compare);
int get_index_by_data(List *plist, void *data, p_traverse pfun_compare);
void sort_list(List *plist);
#endif /* __LIST_H__ */
list.c
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include "sal.h"
List * init_list(void)
{
List *plist = NULL;
plist = (List *)malloc(sizeof(List));
if (NULL == plist) {
return NULL;
}
plist->head = NULL;
plist->tail = NULL;
plist->size = 0;
return plist;
}
Node * create_node(void *data, p_malloc pfun_malloc, size_t size)
{
if (NULL == data) {
return NULL;
}
Node *pnode = NULL;
pnode = (Node *)malloc(sizeof(Node));
if (NULL == pnode) {
return NULL;
}
pnode->data = pfun_malloc(size);
if (pnode->data == NULL) {
free(pnode);
return NULL;
}
memcpy(pnode->data, data, size);
pnode->next = NULL;
return pnode;
}
int delete_node(Node *pnode, p_free pfun_free)
{
SAL_CHECK_P_NULL(pnode);
pfun_free(pnode->data);
free(pnode);
return SAL_OK;
}
int delete_list(List *plist, p_free pfun_free)
{
SAL_CHECK_P_NULL(plist);
Node *pnode = plist->head;
for (; pnode != NULL; pnode = plist->head) {
plist->head = plist->head->next;
delete_node(pnode, pfun_free);
}
free(plist);
return SAL_OK;
}
int add_node(List *plist, Node *pnode)
{
SAL_CHECK_P_NULL(pnode);
SAL_CHECK_P_NULL(plist);
if (NULL == plist->head) {
plist->head = pnode;
plist->tail = pnode;
}
else {
plist->tail->next = pnode;
plist->tail = pnode;
}
plist->size++;
return SAL_OK;
}
Node * get_node_by_index(List *plist, int index)
{
if (NULL == plist
|| NULL == plist->head
|| index < 1
|| index > plist->size) {
return NULL;
}
Node *pnode = plist->head;
for (int i = 1; i < index; i++) {
pnode = pnode->next;
}
return pnode;
}
int remove_node(List *plist, p_free pfun_free)
{
SAL_CHECK_P_NULL(plist);
if (plist->size == 1) {
delete_node(plist->tail, pfun_free);
plist->head = NULL;
plist->tail = NULL;
}
else {
Node *pnode = get_node_by_index(plist, plist->size - 1);
if (NULL == pnode) {
return SAL_ERR;
}
delete_node(plist->tail, pfun_free);
plist->tail = pnode;
plist->tail->next = NULL;
}
plist->size--;
return SAL_OK;
}
int remove_node_by_data(List *plist, void *data, p_traverse pfun_compare, p_free pfun_free)
{
SAL_CHECK_P_NULL(plist);
SAL_CHECK_P_NULL(data);
if (plist->size == 1 && 0 == pfun_compare(plist->head->data, data)) {
remove_node(plist, pfun_free);
}
else {
Node *pnode = plist->head;
for (Node *pprior = NULL; pnode != NULL; pprior = pnode, pnode = pnode->next) {
if (0 == pfun_compare(pnode->data, data)) {
pprior->next = pnode->next;
if (pnode == plist->tail) {
plist->tail = pprior;
}
delete_node(pnode, pfun_free);
plist->size--;
}
}
}
return SAL_OK;
}
int get_index_by_data(List *plist, void *data, p_traverse pfun_compare)
{
SAL_CHECK_P_NULL(plist);
SAL_CHECK_P_NULL(data);
Node *pnode = plist->head;
for (int index = 1; pnode != NULL; index++, pnode = pnode->next) {
if (0 == pfun_compare(pnode->data, data)) {
return index;
}
}
return 0;
}
Node * find_node_by_data(List *plist, void *data, p_traverse pfun_compare)
{
if (NULL == plist || NULL == data) {
return NULL;
}
#if 0
int index = get_index_by_data(plist, data);
if (index > 0) {
Node *pnode = NULL;
pnode = get_node_by_index(plist, index);
if (pnode != NULL) {
return pnode;
}
}
#else
Node *pnode = plist->head;
for (; pnode != NULL; pnode = pnode->next) {
if (0 == pfun_compare(pnode->data, data)) {
return pnode;
}
}
#endif
return NULL;
}
int traverse_list(List *plist, void *data, p_traverse pfun_traverse)
{
SAL_CHECK_P_NULL(plist);
Node *pnode = plist->head;
for (; pnode != NULL; pnode = pnode->next) {
pfun_traverse(pnode->data, data);
}
return SAL_OK;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "list_custom.h"
int main()
{
List *students;
/* 初始化一个空链表 */
students = init_list();
struct Student std[5] = {
{"李明", 2001},
{"王强", 2002},
{"徐晓丽", 2003},
{"张强", 2004},
{"李伟", 2005}
};
/* 将结构体数据装载到链表中 */
for (int i = 0; i < 5; i++) {
Node *student = create_node(std + i, student_malloc, sizeof(struct Student));
add_node(students, student);
}
/* 遍历链表,打印学生信息 */
printf("\r\n------学生排名成绩表-------\r\n");
traverse_list(students, NULL, student_print);
printf("-------------------------\r\n\r\n");
/* 假设链表的顺序是按照成绩排名的,取得第3名的学生信息 */
Node *pnode = get_node_by_index(students, 3);
struct Student * stu3 = (struct Student *)(pnode->data);
printf("第三名同学信息 name : %s, id : %d\r\n", stu3->name, stu3->id);
/* 找到名字叫张强的学生信息 */
pnode = find_node_by_data(students, "张强", stu_name_compare);
if (NULL == pnode) {
printf("查无此人(张强)\r\n");
}
else {
struct Student * stu = (struct Student *)(pnode->data);
printf("张强 name : %s, id : %d\r\n", stu->name, stu->id);
}
/* 假设链表的顺序是按照成绩排名的,取得李明同学的名次 */
int rank = get_index_by_data(students, "李明", stu_name_compare);
if (rank < 1) {
printf("李明未上榜\r\n");
}
else {
printf("李明的排名: %d\r\n", rank);
}
/* 王强同学考试作弊,踢出成绩排名表单 */
remove_node_by_data(students, "王强", stu_name_compare, student_free);
printf("\r\n------学生排名成绩表-------\r\n");
traverse_list(students, NULL, student_print);
printf("-------------------------\r\n\r\n");
delete_list(students, student_free);
/* 初始化工人信息链表 */
List *worker = init_list();
return 0;
}
list_custom.h
#ifndef __LIST_CUSTOM_H__
#define __LIST_CUSTOM_H__
#include <stdio.h>
/* 在这里定义自己的链表Data内容 */
struct Student {
char name[20];
int id;
};
/* eg: 员工薪酬Data */
struct Worker {
char name[20]; //姓名
long payment; //薪酬
char tele[12]; //电话
};
/* 学生信息表自定义处理HANDLE */
void *student_malloc(size_t size);
int student_print(void *nodeData, void *data);
int stu_name_compare(void *nodeData, void *data);
void student_free(void *data);
/* 在这里定义Data处理Handle,eg: Worker*/
#endif /* __LIST_CUSTOM_H__ */
list_custom.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "list_custom.h"
void *student_malloc(size_t size)
{
struct student *stu = (struct student *)malloc(size);
return stu;
}
int student_print(void *nodeData, void *data)
{
struct Student *stu = (struct Student *)(nodeData);
printf("%s %d\r\n", stu->name, stu->id);
}
int stu_name_compare(void *nodeData, void *data)
{
char * name1 = (char *)nodeData;
char * name2 = (char *)data;
if (0 == strcmp(name1, name2)) {
return 0;
}
return -1;
}
void student_free(void *data)
{
struct Student *stu = (struct Student *)data;
free(stu);
}
链表测试结果
river@SHIELD-HJ:/mnt/f/Code/Code/clist$ ./test
------学生排名成绩表-------
李明 2001
王强 2002
徐晓丽 2003
张强 2004
李伟 2005
-------------------------
第三名同学信息 name : 徐晓丽, id : 2003
张强 name : 张强, id : 2004
李明的排名: 1
------学生排名成绩表-------
李明 2001
徐晓丽 2003
张强 2004
李伟 2005
-------------------------
要实现remove_node_by_index
可以先用get_node_by_index
然后再remove_node_by_data
。
github连接:https://github.com/duapple/Code/tree/master/clist
高级版
list.h
#ifndef __LIST_H__
#define __LIST_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#define CLIST_OK 0
#define CLIST_ERR 1
#define CLIST_ERR_MEM 2
#define CLIST_ERR_PTR_NULL 3
#define CLIST_ERR_OPERATION 4
#define CLIST_ERR_NULL -1
#ifndef CLIST_CHECK_PTR_NULL
#define CLIST_CHECK_PTR_NULL(p) \
if (!p) \
{ \
return CLIST_ERR_PTR_NULL; \
}
#endif
typedef struct node_t Node;
struct node_t
{
void *data;
Node *next;
};
typedef struct
{
void (*user_data_free)(void *data); // 用户释放数据,不包含*data的释放
int (*node_compare)(void *node_data, void *data); // 用户数据和节点数据比较,相等返回0
} clist_user_hooks_t;
typedef struct list_t List;
struct list_t
{
Node *head;
Node *tail;
int size;
clist_user_hooks_t user_hooks;
/* private */
int (*delete_node)(List *plist, Node *pnode);
Node * (*create_node)(List *plist, void *data, size_t size);
/* public */
void (*clist_delete_list)(List *plist);
int (*node_add)(List *plist, void *data, size_t size);
int (*node_del)(List *plist);
int (*node_del_by_data)(List *plist, void *data, int (*p_traverse)(void *node_data, void *data));
void * (*clist_get_data_by_index)(List *plist, int index);
void * (*clist_get_data_by_data)(List *plist, void *data, int (*p_traverse)(void *node_data, void *data));
int (*clist_get_index_by_data)(List *plist, void *data, int (*p_traverse)(void *node_data, void *data));
int (*clist_traverse_list)(List *plist, void *data, int (*p_traverse)(void *node_data, void *data));
};
List * clist_new_list(clist_user_hooks_t *hooks);
#ifdef __cplusplus
}
#endif
#endif /* __LIST_H__ */
list.c
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define LIST_LOG_INFO(...) // printf(__VA_ARGS__)
int clist_delete_node(List *plist, Node *pnode)
{
CLIST_CHECK_PTR_NULL(pnode);
plist->user_hooks.user_data_free(pnode->data); // 释放用户分配的数据
free(pnode->data); // 释放节点数据空间
free(pnode); // 释放节点
pnode = NULL;
return CLIST_OK;
}
Node * clist_create_node(List *plist, void *data, size_t size)
{
if (!data)
{
return NULL;
}
Node *pnode = NULL;
pnode = (Node *)malloc(sizeof(Node));
if (!pnode)
{
return NULL;
}
pnode->data = malloc(size);
if (!pnode->data)
{
free(pnode);
return NULL;
}
memcpy(pnode->data, data, size);
plist->user_hooks.user_data_free(data);
pnode->next = NULL;
return pnode;
}
void clist_delete_list(List *plist)
{
if (plist == NULL)
return ;
Node *pnode = plist->head;
for (; pnode != NULL; pnode = plist->head)
{
plist->head = plist->head->next;
plist->delete_node(plist, pnode);
}
free(plist);
plist = NULL;
}
int clist_node_add(List *plist, void *data, size_t size)
{
CLIST_CHECK_PTR_NULL(data);
CLIST_CHECK_PTR_NULL(plist);
Node *pnode = plist->create_node(plist, data, size);
if (pnode == NULL) {
return CLIST_ERR_PTR_NULL;
}
if (NULL == plist->head)
{
plist->head = pnode;
plist->tail = pnode;
}
else
{
plist->tail->next = pnode;
plist->tail = pnode;
}
plist->size++;
return CLIST_OK;
}
int clist_node_del(List *plist)
{
CLIST_CHECK_PTR_NULL(plist);
if (plist->size == 1)
{
plist->delete_node(plist, plist->tail);
plist->head = NULL;
plist->tail = NULL;
}
else
{
Node *pnode = plist->clist_get_data_by_index(plist, plist->size - 1);
if (!pnode)
{
return CLIST_ERR_PTR_NULL;
}
plist->delete_node(plist, plist->tail);
plist->tail = pnode;
plist->tail->next = NULL;
}
plist->size--;
return CLIST_OK;
}
int clist_node_del_by_data(List *plist, void *data, int (*p_traverse)(void *node_data, void *data))
{
CLIST_CHECK_PTR_NULL(plist);
CLIST_CHECK_PTR_NULL(data);
if (plist->size == 1 && p_traverse(plist->head->data, data) == 0)
{
if (plist->node_del(plist) != CLIST_OK)
{
return CLIST_ERR_OPERATION;
}
}
else
{
Node *pnode = plist->head;
for (Node *pprior = NULL; pnode != NULL; pprior = pnode, pnode = pnode->next)
{
if (p_traverse(pnode->data, data) == 0)
{
pprior->next = pnode->next;
if (pnode == plist->tail)
{
plist->tail = pprior;
}
plist->delete_node(plist, pnode);
plist->size--;
}
}
}
return CLIST_OK;
}
void * clist_get_data_by_index(List *plist, int index)
{
if (!plist || !plist->head || index < 1 || index > plist->size)
{
return NULL;
}
Node *pnode = plist->head;
for (int i = 1; i < index; i++)
{
pnode = pnode->next;
}
return pnode->data;
}
void * clist_get_data_by_data(List *plist, void *data, int (*p_traverse)(void *node_data, void *data))
{
if (!plist || !data)
{
return NULL;
}
Node *pnode = plist->head;
for (; pnode != NULL; pnode = pnode->next)
{
if (p_traverse(pnode->data, data) == 0)
{
return pnode->data;
}
}
return NULL;
}
int clist_get_index_by_data(List *plist, void *data, int (*p_traverse)(void *node_data, void *data))
{
CLIST_CHECK_PTR_NULL(plist);
CLIST_CHECK_PTR_NULL(data);
Node *pnode = plist->head;
for (int index = 1; pnode != NULL; index++, pnode = pnode->next)
{
if (p_traverse(pnode->data, data) == 0)
{
return index;
}
}
return 0;
}
int clist_traverse_list(List *plist, void *data, int (*p_traverse)(void *node_data, void *data))
{
CLIST_CHECK_PTR_NULL(plist);
Node *pnode = plist->head;
for (; pnode != NULL; pnode = pnode->next)
{
p_traverse(pnode->data, data);
}
return CLIST_OK;
}
List * clist_new_list(clist_user_hooks_t *user_hooks)
{
if (user_hooks == NULL || user_hooks->user_data_free == NULL || user_hooks->node_compare == NULL)
return NULL;
List *plist = (List *)malloc(sizeof(List));
if (plist == NULL) {
return NULL;
}
plist->head = NULL;
plist->tail = NULL;
plist->size = 0;
plist->user_hooks.user_data_free = user_hooks->user_data_free;
plist->user_hooks.node_compare = user_hooks->node_compare;
plist->create_node = clist_create_node;
plist->delete_node = clist_delete_node;
plist->clist_delete_list = clist_delete_list;
plist->node_add = clist_node_add;
plist->node_del = clist_node_del;
plist->node_del_by_data = clist_node_del_by_data;
plist->clist_get_data_by_data = clist_get_data_by_data;
plist->clist_get_data_by_index = clist_get_data_by_index;
plist->clist_get_index_by_data = clist_get_index_by_data;
plist->clist_traverse_list = clist_traverse_list;
return plist;
}