c语言是如何实现泛型链表

  最近有看一点Linux内核源码,发现内核里大量使用了list_head结构体。百度查了一下,原来内核利用这个结构体实现了泛型。

  自认为对链表已经很熟悉的我,决定自己实现一下。

  下面以Node和list_head为例。

  上图就是循环链大致思路了。(画的不好)

  我们通过list_head对链表进行移动操作。

  这里存在几个问题:

    首先通过list_head得到的指针,它指向的list_head的结构体,但我们其实想要使用的是Node结构体。

    再有,我们要设计一个泛型的链表,那么,我就不可以在实现链表时有任何对Node的操作。

  解决办法:

    1、通过计算,找到node结构体的首地址。(我们通过一个宏来实现)

1 #define list_entry(ptr, type, member) 2     ((type *)((char *)(ptr) - (char *)(&(((type *)0)->member))))

    这个宏看起来可能点乱,但我们把思路缕清就不乱了。

我们只知道entry的指针,如何求出Node的指针呢?

如果我们可以知道entry的相对位置,那么

  Node的实际位置  = entry的实际位置 - entry的相对位置。

  entry的相对位置 =  (&(((Node *)0)->entry))   又蒙了么?  这里,我们先将 0转换为(Node *)类型的指针,再用这个指针指向entry,最后取它的地址。这时我们就得到了entry的相对位置。

  宏中把他们强转成char * 是为了进行减法。最后再强转成Node类型,这时我就得到了Node的指针。

2、让用户自己定义特定数据的操作,我们只提供一个函数接口(我们通过函数指针来实现)

  在我们的链表里,只涉及到了list_head 里的内容,所以,不能对Node进行操作。参数也都是list_head的指针。这就需要用户在使用时,通过上面的宏来完成从list_head 到 Node的转换。在稍后例子中会了解到。

源码

dclist_head.h

 1 /*************************************************************************
 2     > File Name: dlist_head.h
 3     > Author: gaozy
 4     > Mail: [email protected]
 5     > Created Time: 2016年12月24日 星期六 10时11分22秒
 6  ************************************************************************/
 7
 8 /*
 9  *这是我自己实现的一个泛型循环链表
10  *使用者只需要把dclist_head_t 这个结构体加入到自己的结构体中就可以使用这个链表了。
11  *在使用时,需要使用者创建一个头节点,之后使用init函数初始化。(当然,你也可以自己对他进行初始化操作)
12  *它很重要,否则无法使用这个链表。
13  *链表给使用者提供了四个函数
14  *init 刚刚已经说过了,我们用它初始化链表
15  *append 这是一个向链表中添加节点的函数,需要我们传入链表的头和新节点的dclist_head_t部分的指针
16  *treaverse 正如火函数名一样,它的作用是遍历链表,它需要我们提供一个函数指针
17  *这个指针的作用是对节点进行操作,参数是一个dclist_head_t 类型的指针,我们同过list_entry这个宏获取
18  *使用者自定义的数据类型。
19  *dc_remove这个函数用来释放链表,类似treaverse函数,需要我们自己实现删除j节点函数。
20  *它会释放链表中的所有节点,只有头结点例外,头需要我们自己释放
21  */
22
23 #ifndef DLIST_HEAD
24 #define    DLIST_HEAD
25
26 #define list_entry(ptr, type, member) 27     ((type *)((char *)(ptr) - (char *)(&(((type *)0)->member))))
28
29 typedef struct dclist_head {
30     struct dclist_head * prev;
31     struct dclist_head * next;
32 } dclist_head_t;
33
34 void init(dclist_head_t *head);
35 void append(dclist_head_t *head, dclist_head_t *node);
36 void treaverse(dclist_head_t *head, void (*pfun)(dclist_head_t *node) );
37 void dc_remove(dclist_head_t *head, void (*pfun)(dclist_head_t *node) );
38
39 #endif

dclist_head.c

 1 /*************************************************************************
 2     > File Name: dclist_head.c
 3     > Author: gaozy
 4     > Mail: [email protected]
 5     > Created Time: 2016年12月24日 星期六 10时19分49秒
 6  ************************************************************************/
 7
 8 #include <stdio.h>
 9 #include <stdlib.h>
10
11 #include "dclist_head.h"
12
13
14 void init(dclist_head_t *head)
15 {
16     if ( head != NULL ) {
17         head->prev = NULL;
18         head->next = NULL;
19     }
20 }
21
22 void append(dclist_head_t *head, dclist_head_t *node)
23 {
24     if ( head == NULL ) {
25         printf("error\n");
26         exit(1);
27     }
28
29     if ( head->prev == NULL && head->next == NULL ) {
30         head->prev = node;
31         head->next = node;
32         node->prev = head;
33         node->next = head;
34     } else {
35         dclist_head_t *tmp = head->prev;
36         tmp->next = node;
37         node->prev = tmp;
38         node->next = head;
39         head->prev = node;
40     }
41 }
42
43 void treaverse(dclist_head_t *head, void (*pfun)(dclist_head_t *node) )
44 {
45     if ( head == NULL || head->next == NULL )
46         return;
47
48     dclist_head_t *tmp = head->next;
49     while ( tmp != head ) {
50         pfun(tmp);
51         tmp = tmp->next;
52     }
53 }
54
55 void dc_remove(dclist_head_t *head, void (*pfun)(dclist_head_t *node) )
56 {
57     treaverse(head, pfun);
58 }

测试代码

main.c

 1 /*************************************************************************
 2     > File Name: main.c
 3     > Author: gaozy
 4     > Mail: [email protected]
 5     > Created Time: 2016年12月24日 星期六 11时11分25秒
 6  ************************************************************************/
 7
 8 #include <stdio.h>
 9 #include <stdlib.h>
10
11 #include "dclist_head.h"
12
13 typedef struct {
14     int id;
15     dclist_head_t entry;
16 } student_t;
17
18 void print(dclist_head_t *ptr)
19 {
20     student_t *stu = list_entry(ptr, student_t, entry);
21     if ( stu == NULL )
22         return;
23     printf("student id = %d\n", stu->id);
24 }
25
26 void free_node(dclist_head_t *ptr)
27 {
28     if (ptr == NULL )
29         return;
30
31     student_t *node = list_entry(ptr, student_t, entry);
32     free(node);
33 }
34
35 student_t* make_node(int id)
36 {
37     student_t *stu = (student_t *)malloc(sizeof(student_t));
38     if ( stu != NULL ) {
39         stu->id = id;
40     }
41
42     return stu;
43 }
44
45 int main(void)
46 {
47     dclist_head_t list;
48
49     init(&list);
50
51     int i;
52     student_t *stu;
53     for ( i=0; i<5; i++ ) {
54         stu = make_node(i);
55         if ( stu != NULL )
56             append(&list, &stu->entry);
57     }
58
59
60     treaverse(&list, print);
61     dc_remove(&list, free_node);
62
63
64     return 0;
65 }

水平有限,还请大神多多指点。

时间: 2024-10-07 06:28:32

c语言是如何实现泛型链表的相关文章

C语言实现非循环双链表节点的删除(带头结点尾结点)

我在之前一篇博客<C语言实现非循环双链表节点的删除(不带头结点)>中详细讲解了不含头尾节点的双链表中删除一个节点,处理过程还是稍显麻烦.自从我们学习使用头尾节点来处理双链表后,删除过程就非常方便.代码上传至 https://github.com/chenyufeng1991/DeleteNodeDoubleLinkedList_HeadList . 核心代码如下: //删除pos位置的节点 int DeletePosList(Node *pHead,Node *pTail,int pos){

IOS开发语言Swift入门连载---泛型

IOS开发语言Swift入门连载-泛型 泛型代码可以让你写出根据自我需求定义.适用于任何类型的,灵活且可重用的函数和类型.它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图. 泛型是 Swift 强大特征中的其中一个,许多 Swift 标准库是通过泛型代码构建出来的.事实上,泛型的使用贯穿了整本语言手册,只是你没有发现而已.例如,Swift 的数组和字典类型都是泛型集.你可以创建一个Int 数组,也可创建一个String 数组,或者甚至于可以是任何其他 Swift 的类型数据数组

深入浅出数据结构C语言版(5)——链表的操作

上一次我们从什么是表一直讲到了链表该怎么实现的想法上:http://www.cnblogs.com/mm93/p/6574912.html 而这一次我们就要实现所说的承诺,即实现链表应有的操作(至于游标数组--我决定还是给它单独写个博文比较好~). 那么,我们的过程应该是怎么样的呢?首先当然是分析需要什么操作,然后再逐一思考该如何实现,最后再以代码的形式写出来. 不难发现,我们希望链表能支持的(基础,可以由此延伸)操作就是: 1.给出第n个元素 2.在第n个元素的后面插入一个元素(包含在最后一个

c语言:编写一个输出链表的函数print

编写一个输出链表的函数print. 解:程序: #include<stdio.h> #include<stdlib.h> #define LEN sizeof(struct Student) struct Student { long num; float score; struct Student *next; }; int n; struct Student *creat()//建立链表的函数 { struct Student *head; struct Student *p1

数据结构之---C语言实现广义表头尾链表存储表示

//广义表的头尾链表存储表示 //杨鑫 #include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <string.h> #define MAXSTRLEN 40 ) typedef char SString[MAXSTRLEN+1]; typedef char AtomType; // 定义原子类型为字符型 typedef enum{ ATOM, LIST // ATOM==0:原

【C语言数据结构】循环单链表

CircleLinkList.h #ifndef CIRCLE_LINK_LIST #define CIRCLE_LINK_LIST //链表节点 typedef struct _CircleLinkListNode {     struct _CircleLinkListNode *next; }CircleLinkListNode; //循环单链表 typedef void CircleLinkList; /*  * 创建循环单链表  * @return 返回循环单链表的指针  */ Cir

C语言实现简单的单向链表(创建、插入、删除)及等效STL实现代码

实现个算法,懒得手写链表,于是用C++的forward_list,没有next()方法感觉很不好使,比如一个对单向链表的最简单功能要求: input: 1 2 5 3 4 output: 1->2->5->3->4 相当于仅仅实现了插入.遍历2个功能(当然遍历功能稍微修改就是销毁链表了) 用纯C写了份测试代码 /* 基本数据结构的定义以及函数的声明 */ typedef int ElemType; typedef struct Node { ElemType elem; struc

泛型链表结构

internal sealed class Node<T> { public T m_data; public Node<T> m_next; public Node(T data) : this(data, null) { } public Node(T data, Node<T> next) { m_data = data; m_next = next;//存储上一个Node<T>结构 } public override string ToString(

数据结构之---C++语言实现图的十字链表存储表示

最近一直忙着考研复习,很久都没有更新博客了,今天写一篇数据结构的存储. //有向图的十字链表存储表示 //杨鑫 #include <iostream> #include <cstdio> #include <stdlib.h> #include <cstring> using namespace std; #define MAX_VERTEX_NUM 20 #define OVERFLOW -2 #define OK 1 typedef int Status