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

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

  下面以Node和list_head为例。

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

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

  这里存在几个问题:

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

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

  解决办法:

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

 #define list_entry(ptr, type, member) \
((type *)((char *)(ptr) - (char *)(&(((type *))->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

 /*************************************************************************
> File Name: dlist_head.h
> Author: gaozy
> Mail: 44523253@qq.com
> Created Time: 2016年12月24日 星期六 10时11分22秒
************************************************************************/ /*
*这是我自己实现的一个泛型循环链表
*使用者只需要把dclist_head_t 这个结构体加入到自己的结构体中就可以使用这个链表了。
*在使用时,需要使用者创建一个头节点,之后使用init函数初始化。(当然,你也可以自己对他进行初始化操作)
*它很重要,否则无法使用这个链表。
*链表给使用者提供了四个函数
*init 刚刚已经说过了,我们用它初始化链表
*append 这是一个向链表中添加节点的函数,需要我们传入链表的头和新节点的dclist_head_t部分的指针
*treaverse 正如火函数名一样,它的作用是遍历链表,它需要我们提供一个函数指针
*这个指针的作用是对节点进行操作,参数是一个dclist_head_t 类型的指针,我们同过list_entry这个宏获取
*使用者自定义的数据类型。
*dc_remove这个函数用来释放链表,类似treaverse函数,需要我们自己实现删除j节点函数。
*它会释放链表中的所有节点,只有头结点例外,头需要我们自己释放
*/ #ifndef DLIST_HEAD
#define DLIST_HEAD #define list_entry(ptr, type, member) \
((type *)((char *)(ptr) - (char *)(&(((type *))->member)))) typedef struct dclist_head {
struct dclist_head * prev;
struct dclist_head * next;
} dclist_head_t; void init(dclist_head_t *head);
void append(dclist_head_t *head, dclist_head_t *node);
void treaverse(dclist_head_t *head, void (*pfun)(dclist_head_t *node) );
void dc_remove(dclist_head_t *head, void (*pfun)(dclist_head_t *node) ); #endif

dclist_head.c

 /*************************************************************************
> File Name: dclist_head.c
> Author: gaozy
> Mail: 44523253@qq.com
> Created Time: 2016年12月24日 星期六 10时19分49秒
************************************************************************/ #include <stdio.h>
#include <stdlib.h> #include "dclist_head.h" void init(dclist_head_t *head)
{
if ( head != NULL ) {
head->prev = NULL;
head->next = NULL;
}
} void append(dclist_head_t *head, dclist_head_t *node)
{
if ( head == NULL ) {
printf("error\n");
exit();
} if ( head->prev == NULL && head->next == NULL ) {
head->prev = node;
head->next = node;
node->prev = head;
node->next = head;
} else {
dclist_head_t *tmp = head->prev;
tmp->next = node;
node->prev = tmp;
node->next = head;
head->prev = node;
}
} void treaverse(dclist_head_t *head, void (*pfun)(dclist_head_t *node) )
{
if ( head == NULL || head->next == NULL )
return; dclist_head_t *tmp = head->next;
while ( tmp != head ) {
pfun(tmp);
tmp = tmp->next;
}
} void dc_remove(dclist_head_t *head, void (*pfun)(dclist_head_t *node) )
{
treaverse(head, pfun);
}

测试代码

main.c

 /*************************************************************************
> File Name: main.c
> Author: gaozy
> Mail: 44523253@qq.com
> Created Time: 2016年12月24日 星期六 11时11分25秒
************************************************************************/ #include <stdio.h>
#include <stdlib.h> #include "dclist_head.h" typedef struct {
int id;
dclist_head_t entry;
} student_t; void print(dclist_head_t *ptr)
{
student_t *stu = list_entry(ptr, student_t, entry);
if ( stu == NULL )
return;
printf("student id = %d\n", stu->id);
} void free_node(dclist_head_t *ptr)
{
if (ptr == NULL )
return; student_t *node = list_entry(ptr, student_t, entry);
free(node);
} student_t* make_node(int id)
{
student_t *stu = (student_t *)malloc(sizeof(student_t));
if ( stu != NULL ) {
stu->id = id;
} return stu;
} int main(void)
{
dclist_head_t list; init(&list); int i;
student_t *stu;
for ( i=; i<; i++ ) {
stu = make_node(i);
if ( stu != NULL )
append(&list, &stu->entry);
} treaverse(&list, print);
dc_remove(&list, free_node); return ;
}

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

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

  1. 数据结构C语言版 有向图的十字链表存储表示和实现

    /*1wangxiaobo@163.com 数据结构C语言版 有向图的十字链表存储表示和实现 P165 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h> ...

  2. 数据结构(c语言第2版)-----了解链表,栈,队列,串

    关于链表我觉得这都是最基本的东西,但是不常见,在实际的应用中很少的使用,了解它会用就OK,不需要研究的那么深,除非做那种内存压缩,存储方面工作. C语言中动态申请空间 malloc() q=(dlin ...

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

    上一次我们从什么是表一直讲到了链表该怎么实现的想法上:http://www.cnblogs.com/mm93/p/6574912.html 而这一次我们就要实现所说的承诺,即实现链表应有的操作(至于游 ...

  4. C语言 复杂的栈(链表栈)

    //复杂的栈--链表栈 #include<stdio.h> #include<stdlib.h> #define datatype int//定义链表栈数据类型 //定义链表栈 ...

  5. Java基础之泛型——使用泛型链表类型(TryGenericLinkedList)

    控制台程序 定义Point类: public class Point { // Create a point from its coordinates public Point(double xVal ...

  6. 基于visual Studio2013解决C语言竞赛题之0808打印链表

     题目

  7. c语言实现两个单链表的交叉合并

    #include<stdio.h> #include<stdlib.h> #include<iostream> using namespace std; struc ...

  8. C++学习(三十五)(C语言部分)之 单链表

    单链表 就好比火车 火车头-->链表头部火车尾-->链表尾部火车厢-->链表的节点火车厢连接的部分-->指针火车中的内容-->链表节点的的数据链表节点包含数据域和指针域数 ...

  9. C语言版本:循环单链表的实现

    SClist.h #ifndef __SCLIST_H__ #define __SCLIST_H__ #include<cstdio> #include<malloc.h> # ...

随机推荐

  1. 理解nginx的配置

    Nginx配置文件主要分成四部分:main(全局设置).server(主机设置).upstream(上游服务器设置,主要为反向代理.负载均衡相关配置)和 location(URL匹配特定位置后的设置) ...

  2. PHP面向对象之魔术方法复习

    魔术方法复习 2014-9-2 10:08:00 NotePad++ By jiancaigege 飞鸿影~========================= 1.__construct() 构造方法 ...

  3. bower使用记录

    每次做项目的时候都不依赖某一个库来开发,每次需要某一个库的时候都是百度进入库官网再找到下载的库,经常会因为官网的改版更新而在里面绕半天找不到想要的版本号,当然直接去github,CDN 都可以找到需要 ...

  4. 将http调用返回json中的有关中文的unicode转换为中文

    在http调用时获取到的json数据中文是乱码的解决方法: 中文转Unicode:HttpUtility.UrlEncodeUnicode(string str);转换后中文格式:"%uxx ...

  5. CCNA网络工程师学习进程(6)vlan相关协议的配置与路由器简单配置介绍

        前面已经介绍了大部分与vlan技术相关的交换机的协议的配置,更深层次的还有STP协议和以太网端口聚合技术,接着还会简单介绍一下路由器的基本应用.     (1)STP(Spanning-tre ...

  6. 深入理解CSS径向渐变radial-gradient

    × 目录 [1]定义 [2]椭圆圆心 [3]椭圆类型 [4]椭圆大小 [5]色标 [6]重复渐变 [7]其他 前面的话 上篇介绍了线性渐变,本文接着介绍径向渐变的内容 定义 径向渐变,实际上就是椭圆渐 ...

  7. IE浏览器下常见的CSS兼容问题

    目录 [1]宽高bug [2]边框bug [3]盒模型bug[4]列表项bug [5]浮动bug [6]定位bug [7]表单bug 宽高bug [1]IE6-浏览器下子元素能撑开父级设置好的宽高 & ...

  8. poj 2187 Beauty Contest(凸包求解多节点的之间的最大距离)

    /* poj 2187 Beauty Contest 凸包:寻找每两点之间距离的最大值 这个最大值一定是在凸包的边缘上的! 求凸包的算法: Andrew算法! */ #include<iostr ...

  9. Datatables 在asp.net mvc中的使用

    前言 最近使用ABP(ASP.NET Boilerplate)做新项目,以前都是自己扩展一个HtmlHelper来完成同步/异步分页,但是有个地方一直不满意,排序太费劲. 以前接触过一点点的Datat ...

  10. 安装android studio报错Failed to install Intel HAXM.

    在安装android studio的过程中,安装到android的模拟器加速器(intel HAXM)这一步时,报错: HAXM是用来管理硬件加速的,估计是用了这个东西模拟器就能告别Eclipse的龟 ...