前言
啊,看不懂题目什么意思吗?来,老夫给你分解一下
Linux内核学习 之 C语言中 以结构体成员找结构体。重点就是加粗的部分。
啊,什么,还是不太理解。说的再详细点:
给你一个结构体成员的地址,让你找这个结构体的首地址。’
举个栗子:
typedef struct node
{
double x;
int d;
char c;
}Node;
给你一个Node型的结构体变量中的一个成员d的地址,让你找这个结构体变量的地址。
啊,还是不懂。。
“给我滚。。。。。。”
废话说的有些多了哈,言归正传,我们知道,在C语言中知道了结构体变量的地址(也就是结构体变量),引用其成员是很简单的,直接用->符号就可以。但是如果给我们这个结构体变量成员的地址(某个中间的地址),让你找这个结构体变量地址(首地址),一般还真不太好找。
最近学习Linux内核的时候,看到内核中很多部分都用到了这个机制(内核中实现这个机制的是一个叫做container of()的函数,准确点叫宏定义函数),所以仔细研究了一下。
原理
网上有很多讲的比较好的教程,我在这就不再造轮子了。简要概括一下。
我们知道,结构体变量中的成员在C语言中是按照顺序一个一个存放的,如果给你一个中间变量d的地址(pd),计算结构体变量的首地址,我们需要知道首地址和这个地址pd之间的地址差(offset)。
那么问题来了,我们如何知道这个offset呢? 你又没给多余的条件?
答:内核编写的大佬们用了一个小trick,把结构体变量首地址先放到0地址处(通过强制地址转换),那么pd的地址不就是等于offset的值了吗。牛逼不?
具体的详细说明可以自行百度,这里可以给大家一个推荐
container of()函数简介
一个具体的小实验
下面来个小实验,满足我那“碰碰碰”的好奇心。
#include<stdio.h>
#define MAXSIZE 100
typedef struct
{
double x;
int d;
char c;
}Node;
int main()
{
Node t,*p,*tp;
int *pd;
int offset;
//结构体成员赋值
t.c = 'a';
t.d = 10;
t.x = 12.5;
pd =&(t.d); //得到目的结构体变量的成员地址(int型)
p = (Node*)0; //p指向虚拟的Node结点(0地址)
offset =(size_t)&(p->d); //得到偏移量
tp = (Node*)((char*)pd-offset); //得到首地址,(注意,这个地方pd需要转化为char*,因为是地址相减)
//不用t变量访问,用得到的结构体变量首地址进行访问
printf("p->data:%d,p->x:%lf,p->c:%c\n",tp->d,tp->x,tp->c);
return 0;
}
输出结果:
其余的不多说,只说中间的一点:
tp = (Node*)((char*)pd-offset);
这一句话为什么要用(char*)强制转化为(char*)类型?
答:因为指针相减见的是代表指针的那个单元长度,并不是真正的地址。如
p–(p指向一个double型的数据);那么p减的是一个double的长度。