对linux内核中container_of(ptr, type, member)宏的理解

前言

        最近在看《linux设备驱动开发详解》遇到了container_of()宏,在此记录自己对container_of宏定义的理解。

一、container_of(ptr, type, member)宏的作用

        该宏的作用是通过结构体成员的地址和结构体类型推导出结构体的地址,type是指结构体的类型,member是成员在结构体中的名字,ptr是该成员在type结构体中的地址。

二、container_of(ptr, type, member)宏解析

        在linux源码的tools\include\linux\kernel.h文件下,container_of()的定义如下:

#ifndef container_of
/**
 * 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)); })
#endif

        在container_of()宏的定义中的offsetof(TYPE, MEMBER)typeof()初学者可能会对其很陌生,所以我们要先从理解offsetof(TYPE, MEMBER)typeof()的作用开始。

(1)offsetof(TYPE, MEMBER)

        本质也是个宏定义,在linux源码的tools\include\linux\kernel.h文件下定义如下:

#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

        offsetof宏中的TYPE是指结构体的类型,MEMBER是指结构体中的某个成员,作用是求出该成员的在该结构体中的偏移量。该宏首先将0(地址0)转化为TYPE *的结构体指针,表示地址为0的结构体指针,然后通过取地址符&((TYPE *)0)->MEMBER)取出该结构体指针中MEMBER成员的地址,最后再将地址值强转为size_t类型(内核中为unsigned long类型)即表示MEMBER成员在结构体中的偏移量。要理解该过程需要了解对结构体的内存分布,如图,结构体的内存分配是连续的,当结构体的地址为0时,成员的地址即为该成员的偏移量。
结构体内存分配图
实例:

#include <stdio.h>

#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

typedef struct _offset
{
	char member_0;
	int member_1;
	char member_2;
}offset;

int main()
{
	printf("%d\n", offsetof(offset, member_0));
	printf("%d\n", offsetof(offset, member_1));
	printf("%d\n", offsetof(offset, member_2));
	
	return 0;
}

输出:
offsetof实例结果输出

(2)typeof()

        typeof()是GNU C中的一个关键字,和sizeof()一样都是C语言中的关键字而不是函数。作用是返回传入数据的类型。
实例:

#include <stdio.h>

int main()
{
	int a = 3;
	typeof(a) b = a; /* 求出a变量的类型,并创建一个b变量 */
	
	printf("a=%d b=%d", a, b);
	
	return 0;
}

输出:
typeof()关键字实例输出

(3)container_of(ptr, type, member)

        了解了offsetof()宏和typeof关键字之后就比较好理解container_of宏的作用了。

const typeof(((type *)0)->member) * __mptr = (ptr)

        该代码的作用实际上是将0转化为type *结构体类型,再取出结构体中的MEMBER成员(type *)0)->member, 再通过typeof关键字获取MEMBER成员的类型,并定义一个MEMBER成员类型的指针const typeof(((type *)0)->member) * __mptr,将传入的ptr指针赋值给__mptr__mptr = (ptr)

(type *)((char *)__mptr - offsetof(type, member));

        该代码是将获取的MEMBER成员地址强转为char *(强转的目的是考虑指针的加减的实质是指针在内存的偏移,偏移量为指针类型所占字节的个数),减去MEMBER成员在type结构体中的偏移量,强转为type *后得到结构体的地址。
实例:

#include <stdio.h>

#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#ifndef container_of
#define container_of(ptr, type, member) ({			\
	const typeof(((type *)0)->member) * __mptr = (ptr);	\
	(type *)((char *)__mptr - offsetof(type, member)); })
#endif

typedef struct _container 
{
	char member_0;
	int member_1;
	char member_2;
}container;

int main(void)
{
	container *a = NULL;
	container b = {'a', 2, 'b'};
	
	/* member_1在实例结构体中的地址  结构体类型 成员名 */
	a = container_of(&b.member_1, container, member_1);
	printf("a->member_0 = %c\n", a->member_0);
	printf("a->member_1 = %d\n", a->member_1);
	printf("a->member_2 = %c\n", a->member_2);
	
	return 0;
}

输出:
container_of宏实例输出

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值