队列的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
.
【完】
- 队列的C语言实现(通过内核链表)
- 链队列的实现 C语言
- 链队列的C语言实现
- 链队列的C语言实现
- 链队列的c语言实现
- c语言:链队列的实现
- 链队列的c语言实现
- 循环队列的实现(C语言)
- 循环队列的实现(C语言)
- 队列(Queue)的C语言实现
- c语言实现多线程下的链表队列
- 队列-C语言-链表的实现方式
- 带头结点的链队列实现(C语言)
- 带头结点的链队列实现(C语言)
- 带头结点的链队列实现(C语言)
- 队列的C语言实现
- C语言队列的实现
- c语言队列的实现
- 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 A banana
- 视频人员行为识别(Action Recognition)
- 简易日历 1
- 安装dubbo的管理控制台
- spring mvc原理--这篇文章讲的比较透彻
- 队列的C语言实现(通过内核链表)
- 类的静态成员的用法
- Matlab的启动方式
- FaceDataset常用的人脸数据库
- 求DAG最长路 板子
- Java异常处理和设计
- jsp内置对象
- Android编译调用FFmpeg API,自己写方法,编译so库
- HDOJ1003(连续最大和)