侵入式单链表的简单实现

通常情况下,单链表的定义是这样子滴,

typedef struct foo_s {
        int             data;
        struct foo_s    *next;
} foo_t;

结构体里包含了链表指针next; 而侵入式单链表却不同,让结构体包含一个通用的链表。看起来是这个样儿滴,

typedef struct list_s {
        struct list_s *next;
} list_t;

typedef struct foo_s {
        int     data;
        list_t  link;
} foo_t;

所有包含了list_t link的结点构成一个单链表。前一节我们详细分析了offsetof, typeof和container_of, 下面给出一个最简单的侵入式单链表实现。

1. list.h

 1 #ifndef _LIST_H
 2 #define _LIST_H
 3
 4 #ifdef  __cplusplus
 5 extern "C" {
 6 #endif
 7
 8 /**
 9  * offsetof - offset of a structure member
10  * @TYPE:       the type of the struct.
11  * @MEMBER:     the name of the member within the struct.
12  */
13 #define offsetof(TYPE, MEMBER) ((size_t)(&(((TYPE *)0)->MEMBER)))
14
15 /**
16  * container_of - cast a member of a structure out to the containing structure
17  * @ptr:        the pointer to the member.
18  * @type:       the type of the container struct this is embedded in.
19  * @member:     the name of the member within the struct.
20  *
21  */
22 #define container_of(ptr, type, member) ({                      23         const typeof( ((type *)0)->member ) *__mptr = (ptr);    24         (type *)( (char *)__mptr - offsetof(type, member) );})
25
26 typedef struct list_s {
27         struct list_s *next;
28 } list_t;
29
30 typedef void (*list_handler_t)(void *arg);
31
32 extern void list_init(list_t **head, list_t *node);
33 extern void list_fini(list_t *head, list_handler_t fini);
34 extern void list_show(list_t *head, list_handler_t show);
35
36 #ifdef  __cplusplus
37 }
38 #endif
39
40 #endif /* _LIST_H */

2. list.c

 1 /*
 2  * Generic single linked list implementation
 3  */
 4 #include <stdio.h>
 5 #include "list.h"
 6
 7 void
 8 list_init(list_t **head, list_t *node)
 9 {
10         static list_t *tail = NULL;
11
12         if (*head == NULL) {
13                 *head = tail = node;
14                 return;
15         }
16
17         tail->next = node;
18         tail = node;
19         node->next = NULL;
20 }
21
22 void
23 list_show(list_t *head, list_handler_t show)
24 {
25         for (list_t *p = head; p != NULL; p = p->next)
26                 show(p);
27 }
28
29 void
30 list_fini(list_t *head, list_handler_t fini)
31 {
32         list_t *p = head;
33         while (p != NULL) {
34                 list_t *q = p;
35                 p = p->next;
36                 fini(q);
37         }
38 }

3. foo.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include "list.h"
 4
 5 typedef struct foo_s {
 6         int     data;
 7         list_t  link;
 8 } foo_t;
 9
10 static void
11 foo_show(void *arg)
12 {
13         list_t *q = (list_t *)arg;
14         foo_t  *p = container_of(q, foo_t, link);
15
16         printf("show (list) %p next (list) %p \t: "
17             "show (node) %p = {0x%x, %p}\n",
18             q, p->link.next, p, p->data, p->link.next);
19 }
20
21 static void
22 foo_fini(void *arg)
23 {
24         list_t *q = (list_t *)arg;
25         foo_t  *p = container_of(q, foo_t, link);
26
27         foo_t *next_nodep = NULL;
28         if (p->link.next != NULL)
29                 next_nodep = container_of(p->link.next, foo_t, link);
30
31         printf("free (node) %p next (node) %p\n", p, next_nodep);
32         p->link.next = NULL;
33         free(p);
34 }
35
36 int
37 main(int argc, char *argv[])
38 {
39         if (argc != 2) {
40                 fprintf(stderr, "Usage: %s <num>\n", argv[0]);
41                 return -1;
42         }
43
44         list_t *head = NULL;
45         for (int i = 0; i < atoi(argv[1]); i++) {
46                 foo_t *p = (foo_t *)malloc(sizeof (foo_t));
47                 if (p == NULL) /* error */
48                         return -1;
49                 p->data = 0x1001 + i;
50
51                 printf("init (node) %p\n", p);
52                 list_init(&head, &p->link);
53         }
54
55         list_show(head, foo_show);
56
57         list_fini(head, foo_fini);
58
59         return 0;
60 }

4. Makefile

CC    = gcc
CFLAGS    = -g -Wall -m32 -std=gnu99

all: foo

foo: foo.o list.o
    ${CC} ${CFLAGS} -o [email protected] $^

foo.o: foo.c
    ${CC} ${CFLAGS} -c $<

list.o: list.c list.h
    ${CC} ${CFLAGS} -c $<

clean:
    rm -f *.o

clobber: clean
    rm -f foo
cl: clobber

5. 编译并运行

$ make
gcc -g -Wall -m32 -std=gnu99 -c foo.c
gcc -g -Wall -m32 -std=gnu99 -c list.c
gcc -g -Wall -m32 -std=gnu99 -o foo foo.o list.o

$ ./foo 3
init (node) 0x88a1008
init (node) 0x88a1018
init (node) 0x88a1028
show (list) 0x88a100c next (list) 0x88a101c     : show (node) 0x88a1008 = {0x1001, 0x88a101c}
show (list) 0x88a101c next (list) 0x88a102c     : show (node) 0x88a1018 = {0x1002, 0x88a102c}
show (list) 0x88a102c next (list) (nil)         : show (node) 0x88a1028 = {0x1003, (nil)}
free (node) 0x88a1008 next (node) 0x88a1018
free (node) 0x88a1018 next (node) 0x88a1028
free (node) 0x88a1028 next (node) (nil)

小结: 在类型为foo_t的结构体中包含了成员变量list_t link, 那么根据link.next的值(本质上是内存地址)就能使用container_of()计算出结构体变量的内存首地址。一旦拿到了结构体变量的内存首地址,访问其内容就易如反掌。

时间: 2024-11-03 19:06:42

侵入式单链表的简单实现的相关文章

侵入式单链表的简单实现(cont)

前一节介绍的侵入式链表的实现的封装性做得不好,因为会让消费者foo.c直接使用宏container_of().这一节对list的定义做了一下改进,如下所示: typedef struct list_s { struct list_s *next; size_t offset; } list_t; 既然链表结点存了offset, 那么就不再需要container_of()了.(注:Solaris的侵入式双向循环链表就是这么实现的) 1. list.h 1 #ifndef _LIST_H 2 #de

c# 单链表实现 简单示例(可复制直接运行)

最近学习数据结构,发现c# 其实和c 的链表的实现差不多的 下面是一段可直接运行的代码 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Threading; 5 6 namespace SingleLinkedList 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 13 //实例调用

单链表的简单操作

单链表是一种最简单的线性表的链式存储结构,单链表又称线性链表,每个数据元素用一个结点来存储,结点分为存放数据元素的data和存放指向链表下一个结点的指针next. 链表实现:(LinkList.h) //单链表 #ifndef LINKLIST_H_ #define LINKLIST_H_ #include <iostream> //using namespace std; template <typename T> struct Node { //数据成员 T data; Nod

单链表的简单c++实现

以下代码只实现了单链表的手动创建以及输出功能 #include<iostream> using namespace std; struct node { int data; node *next; }; class list { public: void creat(); void show(); private: node *head; }; void list::creat() //创建链表 { node *f=new node(); //建立链表的第一个元素 f->data=44;

对带头结点的单链表的简单操作

#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<memory.h> #define DataType int           //int 可以改写为其它数据类型 typedef struct Node { DataType data; struct Node *next; }Node,*pNode;          //定义结点结构体      

一次单链表的简单练习

#include "stdio.h"#include <stdlib.h>//#include "string.h" typedef int elemType ; typedef struct Node{ /* 定义单链表结点类型 */ elemType element; Node *next;}Node; /* 1.初始化线性表,即置单链表的表头指针为空 */void initList(Node *pNode){ pNode = NULL; print

单链表的简单实现

//定义一个节点类 class Node{ int data; Node nextNode; public Node(int data) { // TODO Auto-generated constructor stub this.data = data; } } 链表类: package test; /** * * @author dao * */ // 链表类 public class ListNode { Node firstNode; // 第一个节点 int length = 0; N

2-5-归并链式存储的单链表-线性表-第2章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第2章  线性表 - 归并单链表(链式存储) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SinglyLinkedList.c        相关测试数据下载

C++侵入式链表

C++标准模板库中的list是非侵入式的链表,当我们通过对象来删除容器中的对象时,需要从头到尾查找一次得到iterator,最后通过iterator来删除对象.这样删除容器中的对象时比较缓慢,所以就实现了一个侵入式链表. intrusive_list.h #ifndef _Intrusive_List_H_ #define _Intrusive_List_H_ // 侵入式链表 template<class T> class intrusive_list { public: struct No