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

const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })

#endif

作用:通过结构体成员变量member的地址,反推出member成员所在结构体变量的首地址,ptr指向成员变量member。

解析:

1)({ })是何方神圣?

({ })是GNU C编译器的语法扩展

({ })与逗号表达式类似,结果作为最后一个语句的值

2)typeof是一个关键字吗?

typeof是GNU C编译器的特有的关键字

typeof只在编译期生效,用于得到变量的类型

3)最后的原理

首先通过offsetof计算出成员变量c在结构体中的偏移量;

pc是指向结构体中成员变量c的指针;

(char *)pc 将pc指针强制类型转换为char *,目的就是为了做指针运算

所实现的就是通过结构体中的一个成员变量的地址反推结构体变量的首地址。

现通过代码来说明,

结构体变量s的值就是结构体变量的首地址,通过container_of也可以得到结构体变量的首地址,两者打印出来的值是相同的。

细心的同学可能会发现,container_of宏中的第二行代码有什么用呢,现在我们就用代码说明,它到底有何用处?

我们将宏中的第二行代码删掉,运行结果同样正确,你心中是否会这样想:这行代码完全可以删掉,因为它对运行结果没有什么影响。如果你这么想,那么请继续往下看:

首先使用我们修改的这个宏,即将第二行代码删掉:

编译过后仅仅告诉我们pc指针没有使用,然后接下来运行。

从中可以看出能够正常的运行,但是运行结果不是我们想要的结果,是不对的。

现在我们用内核提供给我们的宏,即没有经过修改的宏:

在进行编译的时候,多出现了一个警告,提示我们类型不兼容。

所以container_of宏中的第二行代码,是为了做类型检查的。

container_of的功能只能用宏来实现,宏其实是由预处理器在编译的时候来进行处理的。预处理器做的是单纯的文本替换,不会进行任何的类型检查。这就有可能导致我们在编写代码的时候由粗心大意而造成的错误,就像上面的这个错误误用了pi指针。这时候,为了增加代码的安全性,为了有一点点的类型检查,所以在linux内核中该宏的定义中加上了const typeof(((type *)0)->member) * __mptr = (ptr);这条语句。

可能还有疑问?

1)不使用({}),使用逗号表达式能否实现       不可以,因为里面有指针的定义,不能存在于逗号表达式中

2)(type *)0)->member  访问了0地址,会导致程序的崩溃吗?

typeof是在编译期有效,在编译期间就可以拿到成员变量的类型了,不用等到运行期间了。在运行的时候,该条语句就不存在了,因此不会导致程序的崩溃。

剖析linux内核中的宏---------container_of的更多相关文章

  1. 剖析linux内核中的宏-----------offsetof

    offsetof用于计算TYPE结构体中MEMBER成员的偏移位置. #ifndef offsetof#define offsetof(TYPE, MEMBER) ((size_t) &((T ...

  2. linux内核中的宏ffs(x)

    linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/include/asm/bitops.h #define ffs(x) ({ unsigned long __ ...

  3. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

  4. Linux内核中的宏:__init and __exit

    ZZ FROM: http://blog.csdn.net/musein/article/details/742609 ======================================== ...

  5. (十)Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  6. Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  7. Linux内核中的常用宏container_of其实很简单【转】

    转自:http://blog.csdn.net/npy_lp/article/details/7010752 开发平台:Ubuntu11.04 编 译器:gcc version 4.5.2 (Ubun ...

  8. Linux内核中的fastcall和asmlinkage宏

    代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...

  9. Linux内核中SPI/I2c子系统剖析

    Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...

随机推荐

  1. Java基本数据类型转换二

    public class TestConvert2 { /** * @param args */ public static void main(String[] args) { // TODO Au ...

  2. MongoDB高级知识(六)

    1. document的关系 多个文档之间在逻辑上可以相互联系,可以通过嵌入和引用来建立联系. 文档之间的关系可以有: 1对1 1对多 多对1 多对多 一个用户可以有多个地址,所以是一对多的关系. # ...

  3. (day49)Django框架简介

    目录 一.Web框架 (一)本质 (1)动静态网页 (2)wsgiref模块 (3)jinja2模块 (4)手动实现思路 (二)python主流框架 (1)Django (2)flask (3)tor ...

  4. 剑指Offer-12.数值的整数次方(C++/Java)

    题目: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方.   保证base和exponent不同时为0 分析: 注意base为0和expo ...

  5. ubuntu下vscode认识 system("pause")的解决办法

    linux下运行c++程序时,希望控制台不会输出后马上消失. 在windows系统下,用如下语句: #include <cstdlib> system("pause") ...

  6. WEB 中的文件下载(待修改、完善)

    在 WEB 开发中,我们会期望用户在点击某个链接的时候,下载一个文件(不管这个文件能不能被浏览器解析,都要下载).以前接触过一种方式,就是在响应 header 中设置 force-download : ...

  7. ASP.NET开发实战——(十三)ASP.NET MVC 与数据库之EF实体类与数据库结构

    大家都知道在关系型数据库中每张表的每个字段都会有自己的属性,如:数据类型.长度.是否为空.主外键.索引以及表与表之间的关系.但对于C#编写的类来说,它的属性只有一个数据类型和类与类之间的关系,但是在M ...

  8. VMware exsi虚拟机磁盘扩容

    创建Linux时分配磁盘空间随着使用的增加,使用率逐渐升高,需要对/root进行扩容,此时需要在添加或者扩展一下磁盘. 查看Linux版本信息 [root@localhost ~]# cat /etc ...

  9. SQLyog 图形化数据库的操作教程

    首先SQLyog作为mysql的图形化操作工具,是一款非常好用的工具. 操作说明 1.打开工具,点击[新建]输入名称.用户名:root,密码是安装时自己设置的(一定要记住的),端口号默认是:3306, ...

  10. H3C 12508 收集诊断信息

        案例:H3C 12508单板卡出现remove状态,需要配合研发收集诊断信息. (此文档只展示研发要求的诊断信息.) 总体:12500交换机返回三种文件----故障时诊断信息,主备单板的日志文 ...