通常我们设计设计链表都是将数据域放在里面,这样每次需要使用链表的时候都需要实现一个链表,然后重新实现它的相关操作,这里参考Linux系统中的设计实现了一个通用的双向链表,只需要在你的结构里面有一个这个链表的域,就可以使用链表的相关操作了。

注意:这个通用的双向链表是参考Linux系统中的实现,它使用了typeof这个功能,所以有些编译器可能不支持。我是再Windows系统中使用MinGW下使用GCC编译的。

////////////////////////////////////////////////////////////////////////////////////////

// list.h


#ifndef _list_h
#define _list_h

typedef struct _list_head {
    struct _list_head *prev,*next;
} list_head;

#define offsetof(TYPE,MEMBER) ( (size_t) &((TYPE*)0)->MEMBER )

#define container_of(ptr,type,member) ({\
    const typeof( ((type*)0)->member ) *__mptr = (ptr);\
    (type*)( (char*)__mptr - offsetof(type,member) );})

#define list_empty(head) ( head->next==0&&head->prev==0 )

/* get the member of list object
 * @ptr        pointer to list_head
 * @type    the type of container which contains list_head field
 * @memeber field name in the container
 * @return    return pointer to the container
 */
#define list_entry(ptr,type,member) container_of(ptr,type,member)

/* add a new node after `head`
 */
void list_add(list_head *head,list_head *node);

/* delete a node
 */
void list_del(list_head *node);

#endif

/////////////////////////////////////////////////////////

// list.c

#include "list.h"

/* add a new node after `head`
 */
void list_add(list_head *head,list_head *node) {
    if(list_empty(head)) {
        head->next = head;
        head->prev = head;
    }
    node->next = head->next;
    node->prev = head;
    head->next->prev = node;
    head->next = node;
}
/* delete a node
 */
void list_del(list_head *node) {
    node->prev->next = node->next;
    node->next->prev = node->prev;
}

///////////////////////////////////////////////////////////////////////////////

// test.c

#include <stdio.h>
#include <assert.h>
#include "list.h"

typedef struct _task {
    int id;
    list_head next;
} task;

#define task_next(t) ( container_of(t->next.next,task,next) )

void task_print(task *t) {
    printf("#%d -> ",t->id);
}

void task_foreach(task *head,void (*callback)(task *)) {
    task *p = head;
    do {
        callback(p);
        p = task_next(p);
    }
    while (p!=head);
}

// use task like a list
void test_list() {
    task t1={1,{0,0}},
        t2={2,{0,0}},
        t3={3,{0,0}},
        t4={4,{0,0}},
        t5={5,{0,0}};

    list_add(&t1.next,&t2.next);
    list_add(&t2.next,&t3.next);
    list_add(&t3.next,&t4.next);
    list_add(&t4.next,&t5.next);

    task_foreach(&t1,task_print);
}

int main(int argc, char *argv[]) {
    test_list();

    return 0;
}

编译运行

    gcc test.c list.h list.c -o test
    .\test.exe

下载代码

通用双向链表的设计(参考Linux系统中的实现)的更多相关文章

  1. 常见linux系统中RPM包的通用命名规则

    本文重点说一下在常见的linux系统中,RPM包通用的命名规则. RPM包的一般格式为:name-version-arch.rpmname-version-arch.src.rpm 例:httpd-2 ...

  2. (转)浅谈 Linux 系统中的 SNMP Trap

    原文:https://www.ibm.com/developerworks/cn/linux/l-cn-snmp/index.html 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前, ...

  3. 在linux系统中安装VSCode(Visual Studio Code)

    在linux系统中安装VSCode(Visual Studio Code) 1.从官网下载压缩包(话说下载下来解压就直接可以运行了咧,都不需要make) 访问Visual Studio Code官网  ...

  4. 用户管理 之 在Linux系统中,批量添加用户的操作流程

    一.阅读此文件您需要掌握的基础知识: <Linux 用户(user)和用户组(group)管理概述><用户(user)和用户组(group)配置文件详解><Linux 用 ...

  5. 用户管理 之 Linux 系统中的超级权限的控制

    在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者.普通用户无法执行的操作,root用户都能完成,所以也被称之为超级管理用户. 在系统中,每个文件.目录和进程,都归属于某一个用户 ...

  6. 获得Unix/Linux系统中的IP、MAC地址等信息

    获得Unix/Linux系统中的IP.MAC地址等信息 中高级  |  2010-07-13 16:03  |  分类:①C语言. Unix/Linux. 网络编程 ②手册  |  4,471 次阅读 ...

  7. 理解Linux系统中的load average(图文版)转

    一.什么是load average? linux系统中的Load对当前CPU工作量的度量 (WikiPedia: the system load is a measure of the amount ...

  8. Linux系统中常见文件系统格式

    Windows常用的分区格式有三种,分别是FAT16.FAT32.NTFS格式. 在Linux操作系统里有Ext2.Ext3.Linux swap和VFAT四种格式. FAT16: 作为一种文件名称, ...

  9. Linux系统中存储设备的两种表示方法

    转:https://blog.csdn.net/holybin/article/details/38637381 一.对于IDE接口的硬盘的两种表示方法: 1.IDE接口硬盘,对于整块硬盘的两种表示方 ...

随机推荐

  1. Linux图形界面和文字界面的切换

    Ctrl+Alt+F1~F6:切换到文字界面 登陆tty1~tty6终端, 运行等级为run level 3 Ctrl+Alt+F7 或者输入命令startX:切换到图形界面,运行等级为run lev ...

  2. Git以及github的使用方法(五),暂存区和工作区

    Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 先来看名词解释. 工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工 ...

  3. Git以及github的使用方法(一)安装并设置git用户

    最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑.不过,慢慢地有人把它移植到了Windows上.现在,Git可以在Linux.Unix.Mac和Window ...

  4. python(38)- 网络编程socket

    一 客户端/服务器架构 即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 美好的愿望: 最常用的软件服务器是 Web 服务器.一台机器里放一些网页或 Web 应用程序 ...

  5. JavaScript插件编写指南

    在编写插件之前,大家要先了解做插件的几个要点: 插件需要满足的条件 一个可复用的插件需要满足以下条件: 插件自身的作用域与用户当前的作用域相互独立,也就是插件内部的私有变量不能影响使用者的环境变量: ...

  6. 利用泛型和反射,管理配置文件,把Model转换成数据行,并把数据行转换成Model

    利用泛型和反射,管理配置文件,把Model转换成数据行,并把数据行转换成Model   使用场景:网站配置项目,为了便于管理,网站有几个Model类来管理配置文件, 比如ConfigWebsiteMo ...

  7. oracle [union.minus.intersect]

    union 两张表的相同字段的数据[记录类型和列数要一致],合并,并且去重 can replace with "in" (但是如果是两个不同的表而且没什么关联的话必须要union了 ...

  8. What is the relationship between Xcode, Swift and Cocoa?

    Xcode is an IDE for developing Swift or Objective-C applications, which can use the Cocoa API (which ...

  9. HTML marquee标签

    marquee语法    <marquee></marquee> 实例一<marquee>Hello, World</marquee> marquee常 ...

  10. Redis 过期键的设置、获取和删除过期时间

    Redis 过期键的设置.获取和删除过期时间 转自http://blog.51cto.com/littledevil/1813956 设置过期 默认情况下键是没有生存时间的,也就是永不过期,除非清空内 ...