队列的C语言实现(通过内核链表)

来源:互联网 发布:在生产实践中用于数据 编辑:程序博客网 时间:2024/06/07 23:52

0. 环境说明

本文的实验环境是:
win7操作系统+Keil 5 IDE.
非常适合嵌入式软件开发

1. 打造自己的“list.h”

在单片机程序开发中,有时候会用到队列。能否设计一个通用的队列呢?我想,可以把内核链表用起来。

以下代码是我从内核里面扒拉出来的,再稍微改改,就可以在工程中使用了。

#ifndef _LIST_H#define _LIST_H/*    /usr/src/linux-headers-4.8.0-36-generic/include/linux/stddef.h *///求偏移量#define offsetof(TYPE, MEMBER)  ((size_t)&((TYPE *)0)->MEMBER)/*        /usr/src/linux-headers-4.8.0-36-generic/include/linux/kernel.h *//** * container_of - cast a member of a structure out to the containing structure * @ptr:    the pointer to the member. * @type:   the type of the container struct this is embedded in. * @member: the name of the member within the struct. * *///小指针转大指针#define container_of(ptr, type, member) ({          \    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \    (type *)( (char *)__mptr - offsetof(type,member) );})/*    /usr/src/linux-headers-4.8.0-36-generic/include/linux/types.h*/struct list_head {    struct list_head *next, *prev;};/*    /usr/src/linux-headers-4.8.0-36-generic/include/linux/list.h */#define LIST_HEAD_INIT(name) { &(name), &(name) }//以下这个宏用来定义并且初始化头结点#define LIST_HEAD(name) \    struct list_head name = LIST_HEAD_INIT(name)//这个函数不知道内核里面有没有,我自己加的static inline void node_init(struct list_head *node){    node->next = node;    node->prev = node;}/* kernel 3.14 */static inline void __list_add(struct list_head *new,                  struct list_head *prev,                  struct list_head *next){    next->prev = new;    new->next = next;    new->prev = prev;    prev->next = new;      //    kernel 4.8中 这句话是 WRITE_ONCE(prev->next, new);}   /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */static inline void list_add(struct list_head *new, struct list_head *head){    __list_add(new, head, head->next); //头插}/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */static inline void list_add_tail(struct list_head *new, struct list_head *head){    __list_add(new, head->prev, head); //尾插}/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */static inline void __list_del(struct list_head * prev, struct list_head * next){    next->prev = prev;    prev->next = next;       //WRITE_ONCE(prev->next, next);}static inline void list_del(struct list_head *entry){    __list_del(entry->prev, entry->next);    node_init(entry); //add by me    //entry->next = LIST_POISON1;    //entry->prev = LIST_POISON2;}/** * list_empty - tests whether a list is empty * @head: the list to test. */static inline int list_empty(const struct list_head *head){    return head->next == head;    //return READ_ONCE(head->next) == head;}   /** * list_for_each    -   iterate over a list * @pos:    the &struct list_head to use as a loop cursor. * @head:   the head for your list. */#define list_for_each(pos, head) \    for (pos = (head)->next; pos != (head); pos = pos->next)/** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos:    the &struct list_head to use as a loop cursor. * @n:      another &struct list_head to use as temporary storage * @head:   the head for your list. */#define list_for_each_safe(pos, n, head) \    for (pos = (head)->next, n = pos->next; pos != (head); \        pos = n, n = pos->next)/** * list_entry - get the struct for this entry * @ptr:    the &struct list_head pointer. * @type:   the type of the struct this is embedded in. * @member: the name of the list_head within the struct. */#define list_entry(ptr, type, member) \    container_of(ptr, type, member)/** * list_first_entry - get the first element from a list * @ptr:    the list head to take the element from. * @type:   the type of the struct this is embedded in. * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */#define list_first_entry(ptr, type, member) \    list_entry((ptr)->next, type, member)/** * list_next_entry - get the next element in list * @pos:    the type * to cursor * @member: the name of the list_head within the struct. */#define list_next_entry(pos, member) \    list_entry((pos)->member.next, typeof(*(pos)), member)/** * list_for_each_entry  -   iterate over list of given type * @pos:    the type * to use as a loop cursor. * @head:   the head for your list. * @member: the name of the list_head within the struct. */#define list_for_each_entry(pos, head, member)              \    for (pos = list_first_entry(head, typeof(*pos), member);    \         &pos->member != (head);                    \         pos = list_next_entry(pos, member))    /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos:    the type * to use as a loop cursor. * @n:      another type * to use as temporary storage * @head:   the head for your list. * @member: the name of the list_head within the struct. */#define list_for_each_entry_safe(pos, n, head, member)          \    for (pos = list_first_entry(head, typeof(*pos), member),    \        n = list_next_entry(pos, member);           \         &pos->member != (head);                    \         pos = n, n = list_next_entry(n, member))/** * list_for_each_entry_from - iterate over list of given type from the current point * @pos:    the type * to use as a loop cursor. * @head:   the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing from current position. *///从pos指向的结构体开始遍历#define list_for_each_entry_from(pos, head, member)             \    for (; &pos->member != (head);                  \         pos = list_next_entry(pos, member))    /** * list_for_each_entry_safe_from - iterate over list from current point safe against removal * @pos:    the type * to use as a loop cursor. * @n:      another type * to use as temporary storage * @head:   the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */#define list_for_each_entry_safe_from(pos, n, head, member)             \    for (n = list_next_entry(pos, member);                  \         &pos->member != (head);                        \         pos = n, n = list_next_entry(n, member))   /** * list_for_each_entry_continue - continue iteration over list of given type * @pos:    the type * to use as a loop cursor. * @head:   the head for your list. * @member: the name of the list_head within the struct. * * Continue to iterate over list of given type, continuing after * the current position. *///从pos的下一个开始遍历#define list_for_each_entry_continue(pos, head, member)         \    for (pos = list_next_entry(pos, member);            \         &pos->member != (head);                    \         pos = list_next_entry(pos, member))/** * list_for_each_entry_safe_continue - continue list iteration safe against removal * @pos:    the type * to use as a loop cursor. * @n:      another type * to use as temporary storage * @head:   the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */#define list_for_each_entry_safe_continue(pos, n, head, member)         \    for (pos = list_next_entry(pos, member),                \        n = list_next_entry(pos, member);               \         &pos->member != (head);                        \         pos = n, n = list_next_entry(n, member))#endif

2. 接口设计

#ifndef _QUEUE_H#define _QUEUE_H#include "list.h"struct queue_info {    struct list_head *head;    void (*push)(struct queue_info *info, struct list_head *new_node);    struct list_head *(*top)(struct queue_info *info);    struct list_head *(*pop)(struct queue_info *info);    int (*for_each)(struct queue_info *info, void (*todo)(struct list_head *node));    int (*is_empty)(struct queue_info *info);};void queue_init(struct queue_info *info,struct list_head * head);void queue_destroy(struct queue_info *info);#endif

struct queue_info中,首先有一个struct list_head *head,这是一个指针,指向链表的头结点。这个头结点需要用户分配空间;其次是队列具有的方法(指向函数的指针)。

  • void (*push)(struct queue_info *info, struct list_head *new_node);
    入队操作

  • struct list_head *(*top)(struct queue_info *info);
    得到队列的首元素(有别于出队)

  • struct list_head *(*pop)(struct queue_info *info);
    出队

  • int (*for_each)(struct queue_info *info, void (*todo)(struct list_head *node));
    遍历队列,todo由用户实现

  • int (*is_empty)(struct queue_info *info);
    判断队列是否为空

3. 具体实现

3.1 入队

static void queue_push (struct queue_info *info, struct list_head *new_node){    list_add_tail(new_node,info->head);}

直接调用内核链表的尾插函数list_add_tail就行。

3.2 得到队首的元素

struct list_head *queue_top(struct queue_info *info){    if (queue_is_empty(info)) {        return NULL; //队列为空    }    else{        return info->head->next;    }   }

因为是通用队列,无法预测队列中元素的数据形态,所以返回指向struct list_head的指针。为了得到数据,需要用户自己转换(通过宏container_of).

3.3 出队

struct list_head *queue_pop(struct queue_info *info)                   {    if (queue_is_empty(info)) {        return NULL; //队列为空    }    else{        struct list_head *temp = info->head->next;        list_del(temp); //删除队列的首元素        return temp;    }}

注意,list_del(temp);这句话仅仅使队首元素脱离链表,队首元素的空间需要用户自己回收。

3.4 遍历队列的每个元素

static int queue_for_each(struct queue_info *info, void (*todo)(struct list_head *node)){    if(queue_is_empty(info)){        printf("the queue is empty\n");        return -1;    }    else{        struct list_head *pos = NULL;        struct list_head *n = NULL;        list_for_each_safe(pos, n, info->head)            todo(pos);        return 0;    }}

如果队列为空,打印出错信息并返回-1;否则,调用用户传入的todo函数,对每个元素进行操作(list_for_each_safe是内核链表的安全遍历,用普通遍历也是可以的,因为todo函数一般不会进行删除。删除没有道理啊,我实在想不出应用场景)。

其实,我设计这个接口的初衷是为了测试,比如打印队列每个元素,看看入队顺序是否正确等。

3.5 队列的初始化

void queue_init(struct queue_info *info,struct list_head * head){    info->head = head;    node_init(head); //头结点的next和prev都指向自身    info->push = queue_push;    info->pop = queue_pop;    info->top = queue_top;    info->is_empty = queue_is_empty;    info->for_each = queue_for_each;}

此函数应该在最初调用。用户需要定义struct queue_info结构体和struct list_head结构体,然后传入二者的地址。

3.6 队列的析构

void queue_destroy(struct queue_info *info){}

我想了想,觉得此函数只能为空。理由是:
1. 不需要回收空间,所以真的没啥可以做的;
2. 本打算不断出队直到为空,发现出队是用户的事情,不需要越俎代庖。

4. 测试代码及结果

#include "stdio.h"#include "queue.h"#define NAME_MAX_LEN 20struct data_info {    char name[NAME_MAX_LEN];    int age;    struct list_head list;};//此函数用于打印结点信息void  print_node(struct list_head *node){    struct data_info *pdata;    pdata = container_of(node, struct data_info, list);    printf("name:%s, age:%d\n",pdata->name, pdata->age);}int main(void){    struct data_info s[] = {        {"A", 34},        {"B", 42},        {"C", 36},        {"D", 100},        {"E", 18},    };    struct list_head head;    struct queue_info queue;    queue_init(&queue,&head);    //测试入队    int i;    for (i = 0; i < sizeof s/ sizeof s[0]; ++i)     {        queue.push(&queue,&s[i].list);    }    //测试遍历    queue.for_each(&queue,print_node);    //测试top    printf("top method\n");    struct list_head *p_node = queue.top(&queue);    if(p_node==NULL){        printf("top test failed\n");    }    else{        print_node(p_node);    }    //再次遍历,验证top并不是出队       queue.for_each(&queue,print_node);    //测试出队    while(!queue.is_empty(&queue)){        p_node = queue.pop(&queue);        printf("out queue:");        print_node(p_node);    }    while(1);//单片机程序,没有操作系统,只能在这里死循环了   }

在keil5上调试(use simulator),测试结果截图如下:

这里写图片描述

5. 需要注意的问题

普通的编译器是无法编译list.h的,必须支持gnu语法才行。对于Keil,可以添加对gnu的支持,如下图,输入--gnu.

这里写图片描述

【完】

原创粉丝点击