rt_thread源码学习笔记1 -- 已知结构体内部成员和其指针反推结构体首地址

/* 代码如下 */
#define rt_container_of(ptr, type, member)\
        ( (type*)( (char*)(ptr) - (unsigned long)( &((type*)0)->member ) ) )

下面来分析一下:
通过将0强制转化为指向type类型的指针后,对其进行->操作,我们就可以获得在0地址开始定义的type结构体内某个成员的地址,接下来将该地址转化为offset,offset偏移是一个数,又因为是32机器,所以用(unsigned long),并且offset每加一,就代表多偏移一个字节,分析到这我们解释完了(unsigned long)(&((type*)0)->member)
那么(char*)(ptr)是用来干嘛的呢,为什么不直接ptr - (unsigned long)(&((type*)0)->member)呢;
回想一下指针和数的运算,指针减去一个数后变成什么地址是要看指针的类型的,假设一个指向16位整型的指针减去1,那么地址会减2,因为16位占用两个字节,结合上面所分析的offset中的1代表的是一个字节的偏移,那么什么类型的指针加减1代表一个字节的偏移呢?当然是(char*)
最后使用(type*)将偏移后的(char*)转化为指向type类型的指针,就大功告成了。

可能的疑问:
1. 某些书不是讲指针操作很危险,为什么可以直接将0转化为(type*)指针啊?
    首先要明白指针操作危险在哪,指针解引用用作左值是很危险的,因为一旦地址错了,
    你就把原来的正常值给篡改了,但是看看上面代码,里面没有解引用(*poniter),
    也就是说,搞了这么多花里胡哨的都是假把式,并没有真正的对指针所指的存储空间
    进行操作,如此一来,谈何危险。
2. (unsigned long)的用途
    解释过了
3. (char*)的用途
    也解释过了
4. 非得将0转化为(type*)吗,其它数可不可以?
    当然可以,但是0最方便,其它数你不得再做一次减法,如下
    &( ((type *)3264)->member ) - 3264
// linux下如何实现?

/*
 * 选自 linux-2.6.7 内核源码
 * filename: linux-2.6.7/include/linux/stddef.h
 */
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/*
 * 选自 linux-2.6.7 内核源码
 * filename: linux-2.6.7/include/linux/stddef.h
 */
#define container_of(ptr, type, member) ({          \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
/*
 * 需要注意,这里采用的宏定义是({})格式,这种格式可以直接用作赋值表达式右值
 * 但是取的值是宏定义中最后一句的值
 * 至于为什么要定义一个__mptr,我猜可能是防止ptr所指内容被意外更改吧
 */

你可能感兴趣的:(rt_thread源码学习笔记1 -- 已知结构体内部成员和其指针反推结构体首地址)