广播网络基础(1)_了解list.h头文件

广播网络基础(1)_了解list.h头文件

基础数据结构封装

一、offsetof函数
# include <stddef.h>
# include <stdio.h>

struct address{
    char name[50];
    char street[50];
    int phone;
};

int main(){
    printf("address 结构中的 name 偏移 = %ld 字节。\n",
    offsetof(struct address,name));
    printf("address 结构中的 street 偏移 = %ld 字节。\n",
    offsetof(struct address,street));
    printf("address 结构中的 phone 偏移 = %ld 字节。\n",
    offsetof(struct address,phone));
    return 0;
}

二、list_entry()函数

在对函数说明之前,先对双向链表进行说明:双向链表通常有一个独立的用于管理链表的链表头,链表头一般不含有实体数据:

list_entry()函数定义如下:

#define list_entry(ptr,type,member)
	(type*)((char *)ptr - (unsigned long)(&((type *)0)->member))
ptr: 指向list_head 类型链表的指针
struct list_head{
    struct list_head *next,*prev;
};
type: 一个结构
member: 结构type中的一个域,类型为list_head
这个宏返回指向type结构的指针
struct listnode{
    struct list_head list;
    int number;
};
/* 简言之,这个函数表示,当我们知道了list_head类型(也就是结构体一个成员的指针),根据这个指针的地址,计算偏移量(结构体,结构体中的成员),相减得到结构体的指针。*/

用上面offset函数举例:

struct address{
    char name[50],
    char street[50],
    int phone;
};
/* 运行结果:
address 结构中的 name 偏移 = 0 字节
address 结构中的 street 偏移 = 50 字节
address 结构中的 phone 偏移 = 100 字节
*/

假设我们知道street位于70字节处(char *)ptr,同时知道street的偏移量是50,从而知道结构体的地址为20。

三、list_for_each_entry函数

Linux源码中一个重要的宏是: list_for_each_entry;

假设下面几个结点,第一个member表示head,list_for_each_entry的作用就是循环遍历每一个pos中的member子项。

它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后 (next 方向) 移动 pos, 直至又回到 head。

# define list_for_each_entry(pos,head,member) \
/* member 是结构体中的子项,第一个 member 是 head, pos 是传入的结构体,依次从这个
结点开始遍历,直到又回到自己,不需要从自己遍历 */
/* 第一句,首先由head(指针结构体)的后继元素,找到head对应的下一个后继指针结构体所在的
链表结构体的指针 */
/* 第二句,根据 -> 的优先级高于 & , 所以先由 pos -> member得到指针结构体,然后得到指针结构体的指针 */
for(pos = list_entry((head)->next, typeof(*pos),member);
 &pos -> member !=(head); 
 pos = list_entry(pos->member.next, typeof(*pos),member));

list_for_each_entry宏是一个for循环语句,for循环的第一个参数就是让 (head)->next 指向member 成员所在数据结构的指针,也就是将 pos 初始化为链表头指向的第一个实体链表成员。

for 的第三句话通过 pos->member.next指针遍历整个链表,当 pos->member.next 再次指向我们的链表头的时候跳出 for 循环。整个过程没有对链表头进行遍历(不需要被遍历),所以使用 list_for_each_entry遍历链表必须从链表头开始。

list_for_each_entry的功能就是遍历以 head 为链表头的实体链表,对其中的成员进行处理;

四、list_for_each_enrty_safe()函数

相对于 list_for_each_entry,list_for_each_entry_safe 用指针q对链表的下一个数据结构进行了临时存储,所以如果在遍历链表的时候需要做删除链表中的当前项操作时,可以安全删除,而不会影响接下来的遍历例程(用指针q可以继续完成接下来的遍历,而list_for_each_entry则无法继续遍历,删除后会导致无法继续遍历)。

# define list_for_each_entry_safe(pos,q,head,member) \
	for(pos = list_entry((head)->next, typeof(*pos),member),
		q = list_entry(pos->member.next,typeof(*pos),member);
		&pos -> member.next != head;
		pos = q, q = list_entry(pos->member.next,typeof(*pos),member));

五、list.h代码

# ifndef _LIST_H_
# define _LIST_H_

# include <stddef.h>

/* 定义指针数据结构,这是双向循环链表中的一个组成部分,对于双向循环
链表而言,需要有数据域,前驱和后继,这里先定义其中一个成员指针
起名为struct list_head 是因为头指针不需要数据元素? */
struct list_head{
	struct list_head *next, *prev;
};

struct listnode{
	struct list_head list;
	int number;
};
// 表示链表内容的数据结构

# define list_empty(list)((list)->next == (list))
/*这种写法用来判断链表是否为空,定义的类型是链表的后继即为自己*/

# define list_entry(ptr,type,number) \
	(type *)((char *)ptr - offsetof(type,member))
/* 这里的(char *)ptr 表示指向 struct list_head list 的指针,
type表示指向整个结构体 struct listnode 的指针,
member用来表示该结构体中的一个成员:
用struct list_head的指针(所在地址) - 在结构体中的偏移量 = 
struct listnode 的指针 
 */

# define list_for_each_entry(pos,head,member)	\
	for(pos = list_enrty((head)->next,typeof(*pos),member); \
		& pos -> member != (head); \
		pos = list_entry(pos->member.next,typeof(*pos),member))
/*定义一个指向内容结构体pos的指针,从指向指针结构体的head变量开始,
一直遍历到回到head为止,访问每一个pos的member成员
for循环初始:从head所指向的下一个元素,其所在的内容结构体指针位置开始,
只要内容结构体的成员,指针结构体的指针不等于head,则继续遍历,访问下一个pos*/	

# define list_for_each_entry_safe(pos,q,head,member) \
	for(pos = list_enrty((head)->next,typeof(*pos),member),
		q = list_entry(pos->member.next,typeof(*pos),member);
		&pos->number != (head); \
		pos = q, q = list_entry(pos->member.next, typeof(*q),member))
/*引入q,记录pos下一个结点的指针,当收到删除当前结点的命令时,操作更
安全*/

static inline void init_list_head(struct list_head *list){
	list->next = list->prev = list;
}
/*初始化空链表,头指针无实体数据*/

static inline void list_insert(struct list_head *new,
	struct list_head *prev,
	struct list_head *next)
{
	next->prev = new;
	prev -> next = new;
	new -> next = next;
	new -> prev = prev;
}
/* 插入new结点*/

static inline void list_delete_entry(struct list_head *entry)
{
	entry -> next ->prev = entry -> prev;
	entry -> prev -> next = entry -> next; 
}
/* 删除*/

static inline void list_add_tail(struct list_head *new,
	struct list_head *head)
{
	list_insert(new,head->prev,head);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值