Linux链表关键词 和 原理
来源:互联网 发布:淘宝线头算做工瑕疵吗 编辑:程序博客网 时间:2024/04/29 06:27
----------------------------------------关键词 --------------------------------------------------------------------------------------------------------
//1,定义,并分配一个链表头.struct stdio_dev {intflags; intext; charname[16]; void *priv; struct list_head list;};static struct stdio_dev devs;INIT_LIST_HEAD(&(devs.list));//2,添加成员. ,2个参数,第一为要添加的新成员,第二个为链表头.static __inline__ void list_add(struct list_head *new, struct list_head *head)list_add_tail(&(_dev->list), &(devs.list));//3,删除成员. 只要知道要删除的成员地址即可//这里只是对链表指针操作,后面应该释放成员内存.list_del(&(dev->list));//4,遍历成员 ,为devs.list为链表头. list是 struct stdio_dev的链表成员名称.struct list_head *pos;struct stdio_dev *dev;list_for_each(pos, &(devs.list)) {dev = list_entry(pos, struct stdio_dev, list);if(strcmp(dev->name, name) == 0)return dev;}
--------------------实现原理-------------------------------------------------------------------------------------------------------------------------------
链表是一种重要的数据结构,应用的非常广泛。链表分为单向链表与双向链表,一般的实现就是在结构体中内嵌指向下一个元素的指针。例如:
- struct name {
- int num;
- ...;
- struct name *next;
- struct name *prev;
- }
list.h中首先定义了这样一个结构体,这个是链表的基本结构:
- struct list_head {
- struct list_head *next, *prev;
- };
- struct my_struct {
- int a;
- int b;
- ...
- struct list_head list;
- }
- #define LIST_HEAD_INIT(name) { &(name), &(name) }
- #define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
- #define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
- } while (0)
定义了一个链表后,最重要的操作是向链表添加元素,与删除元素以及遍历链表。下面先说添加元素的操作:
- 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;
- }
- static inline void list_add(struct list_head *new, struct list_head *head)
- {
- __list_add(new, head, head->next);
- }
- static inline void list_add_tail(struct list_head *new, struct list_head *head)
- {
- __list_add(new, head->prev, head);
- }
向链表添加元素只知道链表的头与新的元素地址就可以了,因为链表是双向循环链表,链表头的前一个元素就是这个链表的最后一个元素。list_add是将新元素添加到链表头的后面,而list_add_tail是将心元素添加到链表的尾部,这两个操作如下图所示:
图 1 list_add操作
图 2 list_add_tail操作
说完了添加元素,下面说一下如何删除链表元素,如下:
- static inline void __list_del(struct list_head *prev, struct list_head *next)
- {
- next->prev = prev;
- prev->next = next;
- }
- static inline void list_del(struct list_head *entry)
- {
- __list_del(entry->prev, entry->next);
- }
list_head链表最重要的特点就是链表操作与结构体分离,用list_head链接的链表结构上如下图所示:
图 3 list_head链表特点
但是我们使用链表的目的不是链表本身,而是内嵌链表的结构体,我们可以方便的遍历list_head结构,如下: 但是这样的程序没有什么作用,我们需要的是结构体。所以需要一种技术使得由list_head的地址找到内嵌他的结构提的地址,这个技术就是内核中顶顶大名的container_of宏,这个宏就是list_head链表的精髓,它的初衷是为了实现由结构体的元素的地址而找到结构体首地址。借鉴这种技术就可以实现我们的目的。如下: 这个宏实现非常的简单,但是往往伟大的思想就蕴藏在简单的代码中,我们来分析一下这个宏。首先他是一个带参数的宏,有三个参数第一个参数是一个地址,他是结构体中元素的地址,第二个参数是结构体类型,第三个参数是,元素在结构体中的名字。我们以list_head链表为例来说明。例如有如下结构: 我们知道了里面的list_head元素的地址为plist,需要找到类型问struct my_struct结构变量的地址。就需要这样调用宏
struct my_struct *ptr = list_entry(plist, struct my_struct, list);
list_entry宏实现的非常巧妙,括号比较多,让我们将这个宏分解来看。
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
最外面的一层括号可以去掉,这是为了防止宏扩展的,去掉如下:
(type *) ((char *)(ptr)-(unsigned long)(&((type *)0)->member))
现在就比较清楚了,首先(type *)是C强制转换操作,就是将后面的的数据转化成type结构的指针。而后面的操作可以再分解
(char *)(ptr) - (unsigned long)(&((type *)0)->member)
这样就是一个减法的操作,前面是一个指针,我们传过去的结构体元素的指针,这里被转化成指向字符的。而后面是一个长整形,可以再分解
(unsigned long) (&((type *)0)->member)
显然这个长整形是一个指针转化的,而这个指针又可以再分解,
&((type *)0)->member
可以看出这个指针是一个变量取地址得到的,这个变量又是什么呢
((type *)0)->member
看起来有点奇怪,不过这个操作是整个宏中最精妙的,他将地址0转化成type类型,接下来又取得这个结构的member元素,member就是我们传进来的参数:元素在结构体中的命名。其实((type *)0)->member取的变量是内容是什么一点都不重要,重要的我们要取这个变量的地址。取完这个地址将它转换成unsigned long类型,这样这个数据就是((type *)0)->member相对与地址0的偏移。回到上面的那个减法,将结构体中元素的地址与他与结构体首地址的偏移相减,不就得到了结构体的地址了吗。整个操作如下图所示:
- for ( mylist = mylist_head->next; mylist != mylist_head; mylist = mylist->next);
- #define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
- struct my_struct {
- int a;
- char b;
- ...
- strcut list_head list;
- }
struct my_struct *ptr = list_entry(plist, struct my_struct, list);
list_entry宏实现的非常巧妙,括号比较多,让我们将这个宏分解来看。
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
最外面的一层括号可以去掉,这是为了防止宏扩展的,去掉如下:
(type *) ((char *)(ptr)-(unsigned long)(&((type *)0)->member))
现在就比较清楚了,首先(type *)是C强制转换操作,就是将后面的的数据转化成type结构的指针。而后面的操作可以再分解
(char *)(ptr) - (unsigned long)(&((type *)0)->member)
这样就是一个减法的操作,前面是一个指针,我们传过去的结构体元素的指针,这里被转化成指向字符的。而后面是一个长整形,可以再分解
(unsigned long) (&((type *)0)->member)
显然这个长整形是一个指针转化的,而这个指针又可以再分解,
&((type *)0)->member
可以看出这个指针是一个变量取地址得到的,这个变量又是什么呢
((type *)0)->member
看起来有点奇怪,不过这个操作是整个宏中最精妙的,他将地址0转化成type类型,接下来又取得这个结构的member元素,member就是我们传进来的参数:元素在结构体中的命名。其实((type *)0)->member取的变量是内容是什么一点都不重要,重要的我们要取这个变量的地址。取完这个地址将它转换成unsigned long类型,这样这个数据就是((type *)0)->member相对与地址0的偏移。回到上面的那个减法,将结构体中元素的地址与他与结构体首地址的偏移相减,不就得到了结构体的地址了吗。整个操作如下图所示:
图 4 list_entry 宏
0 0
- Linux链表关键词 和 原理
- 文本关键词算法原理总结
- linux进程调度关键词
- 关键词ref和关键词out的区别
- 关键词和描述标签
- 关键词和命名标识符
- 外链对网站权重和关键词排名的重要性
- 利用百度点击原理提升关键词排名
- 百度关键词排名浮动的原理分析
- SEO实战:蛋糕原理,抓住核心关键词
- linux wget原理和问题解决
- Linux Apt原理和应用
- linux和window分区原理
- linux操作系统原理和实践
- 关于讯飞Linux API中上传用户词表、联系人、识别语法和关键词的区别
- 2.2关键词和命名规则
- c#关键词implicit和explicit
- 关键词如何布局和优化
- linux文件与目录管理学习
- Leetcode ZigZag Conversion
- ubuntu下使用ImageMagick命令行批处理改图片尺寸
- android 项目中出现红色感叹号的解决方法
- XP系统下Apache 2.0.59 的安装及配置
- Linux链表关键词 和 原理
- Android大图片裁剪终极解决方案(中:从相册截图)
- RMAN转储数据文件报错"ORA-19870 ORA-19502 ORA-27072"
- 【开源推荐】AllJoyn:打造全球物联网的通用开源框架
- glTexImage2D()&gluBuild2DMipmaps()
- Android大图片裁剪终极解决方案(下:拍照截图)
- Java AES 加密 解密 示例
- Apache MINA 2.0 简单应用示例及与Spring的集成
- 【译】如何使用Android MediaStore裁剪大图片