目录
函数作用
我们先看段代码,了解rt_container_of有什么用处:
#include "stdio.h"
#define rt_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
struct STabcde
{
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
};
struct STest
{
uint32_t lentotal;
uint32_t flag;
uint32_t in;
uint32_t out;
uint8_t buf[10];
struct STabcde STdata;
};
void funtion_t1(uint8_t * inputbuf)
{
struct STest *S;
int i;
S = rt_container_of(inputbuf,struct STest,buf[0]);
printf("struct STest S address[0x%08x]\r\n",&S);
printf("struct STest S1.lentotal = %d\r\n",S->lentotal);
for(i = 0;i < S->lentotal;i++)
{
inputbuf[i] = i;
}
}
void funtion_t2(struct STabcde * inputST)
{
struct STest *S;
int i;
S = rt_container_of(inputST,struct STest,STdata);
printf("struct STest S address[0x%08x]\r\n",&S);
for(i = 0;i < S->lentotal;i++)
{
printf("S->buf[%d] = %d\r\n",i,S->buf[i]);
}
}
int main()
{
struct STest S1;
printf("struct STest S1 address[0x%08x]\r\n",&S1);
S1.lentotal = 10;
funtion_t1(S1.buf);
funtion_t2(&S1.STdata);
return 0;
}
最后运行的结果是:
通过上面的代码我们会发现,funtion函数中只传入struct STest S1里面成员变量地址,函数里面通过 rt_container_of获取到的struct STest *S的地址实际上就是主函数中声明的struct STest S1的地址,struct STest *S = &S1。
通过上面的代码就能知道rt_container_of 的作用,即:查找当前结构体成员所在的结构体的首地址
实现方法
问题来了,rt_container_of怎么做到的?
答案是:通过当前成员的地址减去结构体首地址和成员地址的偏移。
ptr - size可以获得父结构体的起始地址Addre1。
ptr 获取
当前成员的地址ptr很好获取 ,就是函数传入的地址。rt_container_of中强制声明为char *类型的地址,强制声明的意义就是让编译器知道ptr应该按字节地址类型来计算。
#define rt_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
回到rt_container_of 函数中:(char *)(ptr)就是做了对应操作,获取了当前成员的地址。
偏移size获取
最关键的结构体首地址和成员地址的偏移,则通过了一些小技巧来获取。
如上图可知:size = member_Address - Address1
假设有个父结构体的首地址在0x100(Address1 = 0x100),那size = member_Address - 0x100
关键来了,如果假设有个父结构体在、的首地址在0(Address1 = 0)呢,
size = member_Address - 0
父类结构体首地址和成员地址的偏移就是:父类结构体其首地址为0时的该成员的地址。
回到rt_container_of 函数中,
#define rt_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
(type *)0意义是强制声明在0地址有个type类型,&((type *)0)->member)就是指向结构体的成员member,通过取址符号&获取其地址,因为当前结构体的首地址是0,所以该结构体的成员的地址就是Size。