• container_of 和 offsetof 宏详解


    在linux内核链表中,会遇到两个宏。

    在include/linux/stddef.h中,有这样的定义

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

    这里的TYPE表示某个结构体类型,MEMBER表示结构体中的一个成员,这个宏求出了成员在结构体中的位置偏移(以字节为单位)

    如果你还不理解,我们举个例子吧。

    struct student 
    {
    	char name[20];
    	unsigned char age;
    };
    
    int main(void)
    {
    	int off = offsetof(struct student, age);
    	printf("off = %d
    ",off);
    }
    
    运行结果是off = 20


    其实宏里面的0只是一种特殊情况而已。对这个宏的解释是:假设结构体处于0x1234这个地址,

    ((TYPE*)0x1234 )-> MEMBER
    ->比类型转换的优先级高,所以要加括号。上面就得到了成员,再取地址,得到成员的地址

    &((TYPE*)0x1234 )-> MEMBER

    不用加括号,因为&的优先级比较低

    再来一个类型转换

    (size_t)&((TYPE*)0x1234 )-> MEMBER

    终于得到成员的起始地址了,还没有完,既然算偏移,就要减去结构体的起始地址

    (size_t)&((TYPE*)0x1234 )-> MEMBER  - 0x1234

    不难看出,这里的0x1234换成什么数字都可以,因为偏移是和起始地址无关的。

    不信的话可以把这个宏定义改一改,再测试一下

    #define offsetof(TYPE, MEMBER) (  (size_t) &((TYPE *)0x2222)->MEMBER  -   0x2222  )
    
    
    struct student 
    {
    	char name[20];
    	unsigned char age;
    };
    
    int main(void)
    {
    	int off = offsetof(struct student, age);
    	printf("off = %d
    ",off);
    }
    
    改成0x2222后,结果还是20.

    既然什么数字都可以,那就改成0吧,于是后面的-0就可以省略了。于是就得到了开头的那个宏。


    下面我们说另外一个宏。

    /**

    827  * container_of - cast a member of a structure out to the containing structure

    828  * @ptr:    the pointer to the member.

    829  * @type:   the type of the container struct this is embedded in.

    830  * @member: the name of the member within the struct.

    831  *

    832  */

    833 #define container_of(ptr, type, member) ({         

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

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


    这个宏就是从一个成员的地址得到这个结构体的地址,俗称小指针转大指针。

    继续举例子。

    struct student 
    {
    	char name[20];
    	unsigned char age;
    };
    
    int main(void)
    {
    	struct student stu = {"wangdong",22};
    	
    	printf("&stu = %p
    ",&stu);
    	printf("&stu.age = %p
    ",&stu.age);
    
    	struct student *p = container_of(&stu.age, struct student, age);
    	printf("p= %p
    ",p);
    }
    运行结果为

    &stu = 0x7fff53df9c40

    &stu.age = 0x7fff53df9c54

    p= 0x7fff53df9c40

    第一行和第三行的值是一致的。

    如果你不理解这个宏的定义,我们先简化一下它,这样写
    #define container_of(ptr, type, member)             (  (type *)( (char *)ptr - offsetof(type,member) )  )
    (char *)ptr 成员的地址
    offsetof(type,member)  成员的偏移
    二者相减,就是结构体的起始地址,最后再加个强制类型转换。
    可是为什么不是这样写的呢,而是要多出来一行

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

    没有这行到底行不行,其实也行,用上面的例子,去掉这行,也可以得到一样的结果。

    先看看这行什么意思吧, ((type *)0)->member 这是成员,typeof( ((type *)0)->member ) 得到了成员的类型,假设就是unsigned char类型,

    const typeof( ((type *)0)->member ) *__mptr 定义了一个这个类型的指针

    const unsigned char * __mptr = (ptr); 赋值给定义的这个指针。

    我苦思冥想,又结合网上的资料,认为这样写是做了一个类型检查。如果有了这行,假设结构体就没有member这个成员,那么编译会报错。

    所以这样写保证了member确实是type的一个成员。

    还有,这样写也保证了ptr确实是这个成员类型的指针,如果不是,编译也会报错。再做个实验。

    把刚才的代码改一下

    struct student *p = container_of((int *)&stu.age, struct student, age);

    故意把&stu.age转换成int*,编译就会报警告:

    test.c:33:22: warning: incompatible pointer types initializing 'const typeof (((struct student *)0)->age)

          *' (aka 'const unsigned char *') with an expression of type 'int *' [-Wincompatible-pointer-types]

            struct student *p = container_of((int *)&stu.age, struct student, age);

                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    test.c:11:39: note:expanded from macro 'container_of'

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

                                                 ^        ~~~~~


    如果没有这一行,那么就不会报错。

    所以,作者的出发意图是千方百计地让程序员写出安全的代码啊!真的是用心良苦。

    (完)

  • 相关阅读:
    Maven仓库是什么
    什么是Maven
    Shiro 的优点
    shiro有哪些组件
    Python偶斐波那契数
    Python求1000以内所有3或5的倍数的和。
    python"TypeError: 'NoneType' object is not iterable"错误解析
    python中列表常用的几个操作函数
    反射类的一些基本用法
    循环随机数短时间内大多都是重复的问题
  • 原文地址:https://www.cnblogs.com/longintchar/p/5224435.html
Copyright © 2020-2023  润新知