zoukankan      html  css  js  c++  java
  • 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: 44523253@qq.com 
     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: 44523253@qq.com 
     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
    ");
    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: 44523253@qq.com 
     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
    ", 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 }

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

  • 相关阅读:
    Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result
    smarty {for}{forelse}
    整理了一份比较全面的PHP开发编码规范.
    安装SQL server 提示重新启动计算机失败
    使用自定义《UIActivity》进行内容分享-b
    五子棋-b
    iOS面试题16719-b
    iOS 图片填充 UIImageView
    将UIImage保存到iOS照片库和对应程序沙盒中-b
    IOS webview中cookie的读取与保存-b
  • 原文地址:https://www.cnblogs.com/ITgaozy/p/6217328.html
Copyright © 2011-2022 走看看