链表的应用

链表在我们的实际开发项目中有着广泛的应用,Linux内核实现了一个精妙的链表,可以方便地嵌入到任何一个数据结构中,而不用为每个数据结构单独写一个插入、删除等操作。

本文参照内核链表实现,实现了一个更加精炼的链表实现,剔除了不常用的操作,保留最常用的插入、删除、遍历操作,重写或修改了链表结构、插入删除函数,使实现更为精炼、清晰。

嵌入位置可以放置在开发者自定义结构体的任何位置,通过该节点的地址和在结构体中的偏移可以定位自定义结构体的地址:

/* 获取自定义结构体的地址 */
#define LIST_ENTRY(ptr, type, member)                           \
        ((type *)((char *)ptr - (size_t)&((type *)0)->member))

文末测试代码嵌入位置放在了结构体的起始位置,由于结构体第一个变量的地址和整体结构体的地址一致,这样方便我们从节点类型到结构体类型的转换,具体见下文的测试用例。

链表和结构体定义:

typedef struct node {       /* node of a linked list. */
    struct node* next;      /* Points at the next node in the list */
    struct node* previous;  /* Points at the previous node in the list */
} NODE, LIST;

链表相关的宏定义:

#define HEAD    next       /* first node in list */
#define TAIL    previous   /* last node in list */
#define LIST_FOR_EACH(type,var,list) \
            for(var = (type*)lstFirst(list); \
            var != (type*)(list); \
            var=(type*)lstNext(&var->node))

/* initializes a list descriptor */
#define LIST_HEAD_INIT(name) { &(name), &(name) } 

实现如下:

/* initializes a list descriptor */
void lstInit(LIST* pList){
    pList->HEAD  = pList;
    pList->TAIL  = pList;
}

/* find first node in list */
NODE* lstFirst(LIST* pList){
    return (pList->HEAD);
}

/* find last node in a list */
NODE* lstLast(LIST* pList){
    return (pList->TAIL);
}

/* find the next node in a list */
NODE *lstNext(NODE *pNode){
    return (pNode->next);
}

/* get the list empty status */
int lstEmpty(LIST* pList){
    return pList->HEAD == pList;
}

/* report the number of nodes in a list */
int lstCount(LIST *pList){
    int count = 0;
    NODE *node = lstFirst(pList);
    for (; node != pList; node = lstNext(node), count++);
    return count;
}

/* insert a node in a list after a specified node */
void lstInsertAfter(NODE* pre, NODE* _new){
    NODE *next = pre->next;
    pre->next = _new;
    _new->next = next;
    _new->previous = pre;
    next->previous = _new;
}

/* add a node to the end of a list */
void lstAdd(NODE* list, NODE* new){
    lstInsertAfter(list->TAIL, new);
}

void __lstDelete(NODE *prev, NODE *next){
    next->previous = prev;
    prev->next = next;
}

/* delete a specified node from a list */
void lstDelete(NODE* entry){
    __lstDelete(entry->previous, entry->next);
    entry->next = NULL;
    entry->previous = NULL;
}

下面我们写一个测试用例,用来测试上述链表。
首先随便模拟一个数据结构。

typedef struct __DATA__ {
    NODE  node;
    int id;
    char data[6];
}DATA;

接下来定义4个数据,孤立的,还未用链表串起来。


static DATA data0 = {
    .node = LIST_HEAD_INIT(data0.node),
    .id = 0,
    .data = "data0"
};

static DATA data1 = {
    .node = LIST_HEAD_INIT(data1.node),
    .id = 1,
    .data = "data1"
};

static DATA data2 = {
    .node = LIST_HEAD_INIT(data2.node),
    .id = 2,
    .data = "data2"
};

static DATA data3 = {
    .node = LIST_HEAD_INIT(data3.node),
    .id = 3,
    .data = "data3"
};

main函数如下:


int main(){
    DATA *var = NULL;
    static LIST list = LIST_HEAD_INIT(list);  /* or use lstInit() to initialize */
    //lstInit(&list);
    
    /* test add elemens */
#if 1
    /* add to tail */
    lstAdd(&list, (NODE*) &data0);
    lstAdd(&list, (NODE*) &data1);
    lstAdd(&list, (NODE*) &data2);
    lstAdd(&list, (NODE*) &data3);
#else 
    /* add to front */
    lstInsertAfter(&list, (NODE*) &data0);
    lstInsertAfter(&list, (NODE*) &data1);
    lstInsertAfter(&list, (NODE*) &data2);
    lstInsertAfter(&list, (NODE*) &data3);
#endif 

    printAddr(&list);
    printf("%d elemens in the list [%p]:\n", lstCount(&list), &list);
    LIST_FOR_EACH(DATA, var, &list){
        printf("id:%d\t%s\t%p\n", var->id, var->data, var);
    }
    printf("========================\n");
    
    /* test deleted elements */
    while (!lstEmpty(&list)){
        DATA * deleted = (DATA *)lstLast(&list);
        printf("id:%d\t%s ==> to be deleted\n", deleted->id, deleted->data);
        lstDelete((NODE*) deleted);
        printf("after deleted: left %d elemens in list\n", lstCount(&list));
        LIST_FOR_EACH(DATA, var, &list){
            printf("id:%d\t%s\t%p\n", var->id, var->data, var);
        }
        printf("========================\n");
    }
    
    return 0;
}

辅助函数printAddr定义如下,主要答应链表地址,便于观察:

void printAddr(LIST* list){
    int timer = 0;
    NODE *node = NULL;
    node = list;
    printf("list addr:%p\n", node);
    /* print 3 times rounds */
    while(timer <= 3){
        node = lstNext(node);
        printf("%p  ", node);
        if (node == list) {
            timer++;
            printf("\n");
        }
    }
    printf("========================\n\n");
}

编译运行,得到如下结果:

ubuntu@Cpl: ~ $ ./a.out
list addr:0x6010c0
0x601040  0x601060  0x601080  0x6010a0  0x6010c0
0x601040  0x601060  0x601080  0x6010a0  0x6010c0
0x601040  0x601060  0x601080  0x6010a0  0x6010c0
0x601040  0x601060  0x601080  0x6010a0  0x6010c0
========================

4 elemens in the list [0x6010c0]:
id:0    data0   0x601040
id:1    data1   0x601060
id:2    data2   0x601080
id:3    data3   0x6010a0
========================
id:3    data3 ==> to be deleted
after deleted: left 3 elemens in list
id:0    data0   0x601040
id:1    data1   0x601060
id:2    data2   0x601080
========================
id:2    data2 ==> to be deleted
after deleted: left 2 elemens in list
id:0    data0   0x601040
id:1    data1   0x601060
========================
id:1    data1 ==> to be deleted
after deleted: left 1 elemens in list
id:0    data0   0x601040
========================
id:0    data0 ==> to be deleted
after deleted: left 0 elemens in list
========================


参考资料:
[1] Linux/list.h

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luuyiran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值