C语言侵入式链表

C语言虽然没有泛型,但是拥有对内存的终极解释权限,这个也带给了C语言强大的表达能力。对于复杂数据结构,侵入式链表绝对是一个好的经典实现,Linux Kernel 在 2.1 之后就使用 list_head 的内嵌来实现双向循环链表。而且,glibc也提供了这种实现的标准库 glibc/include/list.h

Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
– Linus

对于C语言项目,有时候会需要精细规划内存使用。这时设计数据结构时,通常都是存储和控制结构分离。先规划存储,然后规划各种逻辑关系、查找结构。这时候,Data结构里面就会经常的用到侵入式链表。一个Data块被多个链表使用,这种结构在Linux内核、Redis、Nginx都是非常常见。内嵌链表的实现原理也很简单,结构体描述的是一块连续内存的内存布局,知道结构体一个成员的地址,那么就可以偏移到结构体的首地址,标准库里就有offsetof 宏。

举一个例子,一组数据,里面嵌两个链表,一组为正数链表,一组为负数链表。那么就可以在Data中嵌两个链表进去,实现如下:

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>

typedef struct list_head {
    struct list_head *next;
    struct list_head *prev;
} list_t;

/* Define a variable with the head and tail of the list.  */
#define LIST_HEAD(name) \
  list_t name = { &(name), &(name) }

/* Initialize a new list head.  */
#define INIT_LIST_HEAD(ptr) \
  (ptr)->next = (ptr)->prev = (ptr)

/* Add new element at the head of the list.  */
void list_add(list_t *newp, list_t *head) {
    newp->next = head->next;
    newp->prev = head;
    head->next->prev = newp;
    head->next = newp;
}

/* Remove element from list.  */
void list_del(list_t *elem) {
    elem->next->prev = elem->prev;
    elem->prev->next = elem->next;
}

/* Get typed element from list at a given position.  */
#define list_entry(ptr, type, member) \
    ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member)))

/* Iterate forward over the elements of the list.  */
#define list_for_each(pos, head) \
  for (pos = (head)->next; pos != (head); pos = pos->next)

typedef struct tagData {
    int val;
    list_t positive;
    list_t negative;
} Data;

int main() {
    // 存储Data节点
    Data data[10];
    memset(data, 0, sizeof(data));
    size_t i = 0, len = sizeof(data) / sizeof(data[0]);

    LIST_HEAD(positive);
    LIST_HEAD(negative);
    INIT_LIST_HEAD(&positive);
    INIT_LIST_HEAD(&negative);

    // 随机初始化Data的val
    for (i = 0; i < len; i++) {
        data[i].val = (int)random() % 10 - 5;
        if (data[i].val > 0) {
            list_add(&data[i].positive, &positive);
        }
        if (data[i].val < 0) {
            list_add(&data[i].negative, &negative);
        }
    }

    // print 正数链和负数链
    list_t *pos;
    printf("positive number: ");
    list_for_each(pos, &positive) {
        Data *d = list_entry(pos, Data, positive);
        printf("%d ", d->val);
    }
    printf("\n");
    printf("negative number: ");
    list_for_each(pos, &negative) {
        Data *d = list_entry(pos, Data, negative);
        printf("%d ", d->val);
    }

}

好的数据结构设计可以让程序清晰明了。数据结构设计的越完善,代码流程往往也就越清晰简洁。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值