container_of函数原理分析

2024-06-21 13:58
文章标签 分析 函数 原理 container

本文主要是介绍container_of函数原理分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

/**

 * container_of - cast a member of a structureout to the containing structure

 * @ptr:       the pointer to the member.//指向成员变量的指针

 * @type:      the type of the container struct this is embedded in.//结构体的类型

 * @member:    the name of the member within the struct.//成员变量名

 *

 */

#definecontainer_of(ptr, type, member) ({                      \

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

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


每行前面的数字是所在的行数。 可见在这个宏定义中用到了另外两个宏:typeof 和 offsetof 。

对 typeof 的理解:

实际上, typeof 并不是宏定义,它是GCC的关键字,是GCC特有的特性。如果只知道一个变量的名字要得到其类型,并不是宏定义能够完成的,这需要编译时的信息。所 以,typeof 操作是GCC内置的功能,在内核头文件和Glibc头文件中都是找不到typeof的宏定义的。

对 offsetof 的理解:

offsetof 是一个真正的宏,它定义在内核源代码 include/linux/stddef.h 文件中:

第一步得到成员member相对于结构体起始位置的偏移量(就是说相对于结构体的首地址来说往后此成员变量往后偏移了几个字节)

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

在这里,TYPE表示一个结构体的类型,MEMBER是结构体中的一个成员变量的名字。offsetof 宏的作用是计算成员变量 MEMBER 相对于结构体起始位置的内存偏移量,以字节(Byte)为单位。

它的计算原理是,把整数0进行强制类型转换,转换为一个 TYPE 类型的指针; 然后找到成员变量 MEMBER; 最后用取地址符 “&” 得到 MEMBER 的地址。 因为结构体是从地址0处开始的,所以 MEMBER 的地址就是其相对于结构体起始位置的偏移量。第一步完成


第二步:减法操作
对 container_of 的理解:
 

三个参数, ptr是成员变量的指针, type是指结构体的类型, member是成员变量的名字。 container_of 的作用就是在已知某一个成员变量的名字、指针和结构体类型的情况下,计算结构体的指针,也就是计算结构体的起始地址。 计算的方法其实很简单,就是用该成员变量的指针减去它相对于结构体起始位置的偏移量。

493 #define container_of(ptr, type, member) ({                      \
494          const typeof( ((type *)0)->member ) *__mptr = (ptr);    \//成员指针赋值
495          (type *)( (char *)__mptr - offsetof(type,member) );})//减操作


在这个定义中,typeof( ((type *)0)->member ) 就是获得 member 的类型, 然后定义了一个临时的常量指针 __mptr, 指向 member 变量。 为什么不直接使用 ptr 而要多此一举呢? 我想可能是为了避免对 ptr 及prt 指向的内容造成破坏。 把 __mptr 转换成 char * 类型, 因为 offsetof 得到的偏移量是以字节为单位。 两者相减得到结构体的起始位置, 再强制转换成 type 类型。



实质上

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \

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

如果知道type和member 就能获取type的首地址,也就是结构体首地址。

这篇关于container_of函数原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081411

相关文章

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高