详解两个重要宏offsetof和containe…

本文深入解析了结构体中offsetof和container_of两个宏的功能与使用方法。通过对实例的分层解析,详细介绍了如何利用这两个宏计算结构体成员的偏移量及从成员地址找到结构体首地址。
本文解析关于结构体的两个重要宏offsetof( TYPE , MEMBER)和container_of(ptr , type , member)

实例1、#define offsetof( TYPE , MEMBER) (int *)&((TYPE *)0)->MEMBER

(备注:    ->的优先级比&高)
参数说明:TYPE为结构体变量类型;MEMBER为结构体中任意一个元素
作       用:得出该MEMBER元素相对于结构体首地址的偏移量
分层解析:
    (1) ((TYPE*)0):将0强制转换成指向TYPE类型结构体变量的指针,该结构体变量地址被强制变为0x0;
    (2) ((TYPE*)0)->MEMBER:指向结构体中的MEMBER元素;
    (3) (int *)&((TYPE *)0)->MEMBER:取MEMBER元素的地址并强制转换成指向int类型数据的指针。
实       质:将结构体变量地址强制变成0x0,当得出其中某个元素地址后,由于结构体内元素地址是逻辑连续的,该元素地址被强制转换成(int *)后即可被理解为偏移量。(见代码示例1)

实例2、#define container_of(ptr , type , member) ({  const typeof(((type *)0)->member) *__mptr = (ptr) ; (type *)((char *)__mptr - offsetof(type , member ) );   })

参数说明:ptr为指向member元素的指针;type为结构体变量类型;member为结构体中任意一个元素
作       用:通过结构体中某一元素的地址得出结构体(变量)首(的)地址;
分层解析:
    (1) typeof(((type *)0)->member)得出member元素的数据类型;
    (2) const typeof(((type *)0)->member) *__mptr定义一个和member同数据类型的指针,指针指向常量;
    (3) __mptr = (ptr) 将指向member元素的指针赋给新定义的__mptr指针;
    (4) (char *)__mptr - offsetof(type , member )该元素地址减去自身相对结构体地址偏移量得出初始地址;
    (5) (type *)((char *)__mptr - offsetof(type , member ) )将初始地址强制转换成结构体变量地址。
实       质:当传参数ptr指针时,编译器只传ptr的值,并没传它的数据类型,所以需要通过typeof()函数获取它的类型;得出member元素的地址和类型后,减去它的偏移量,即可得出初始地址,即为结构体首地址。(见代码示例2)

代码示例(1)
#include
#define offset(TYPE,MEMBER) ((int)&(((TYPE *)0)->MEMBER))
struct mystruct
{
char a;
};
int main ( )
{
struct  mystruct s1;
int offsetof_a = offset(struct mystruct,a);
printf("offsetof_a = %d\n",offsetof_a);
}

代码示例(2)
#include
#define offsetof(TYPE,MEMBER)   ((int)&(((TYPE *)0)->MEMBER))    //算结构体中元素的地址偏移量
#define container_of(ptr,type,member)   ({ const typeof(((type *)0)->member) *__mptr = (ptr);\
(type *)((char *)__mptr - offsetof(type,member));  })   //得出结构体首地址
// 宏定义中'\'表示连接上下行
struct mystruct
{
char a;
short c;
};
int main ()
{
struct mystruct s1;
struct mystruct *p = NULL;
short *p1 = &s1.c;
printf("s1的指针 = %p\n",&s1);   //此句可得到结构体首地址,前提是定义了变量s1;

//但在Linux内核中,往往不知道结构体变量的,那该如何得到呢?请看下面
//现在要通过p来计算得到s1的指针
p1 = container_of(p,struct mystruct,c);
printf("ps的指针 = %p\n",p1);   //地址值和&s1一样
}


(原创,如转载,请说明本文出处)






### 定义 `offsetof` 的用法与含义 定义 `offsetof` 是 C C++ 中用于计算结构体成员相对于结构体起始地址的偏移量的一种工具。它返回的结果是以字节为单位的偏移量,通常用于内存操作或低级编程场景中[^1]。 在标准头文件 `<stddef.h>` 中,`offsetof` 的定义如下: ```cpp #define offsetof(s, m) (size_t)&(((s*)0)->m) ``` #### 定义解释 - `(s*)0`:将 0 强制转换为类型 `s` 的指针,表示一个虚拟的结构体实例。 - `->m`:访问该虚拟结构体中的成员 `m`。 - `(size_t)&`:取成员 `m` 的地址,并将其转换为 `size_t` 类型,从而得到以字节为单位的偏移量。 这种实现方式通过假设结构体起始地址为 0 来计算成员相对于结构体起始位置的偏移量。需要注意的是,这种方式不会实际分配内存,因此不会引发运行时错误[^2]。 #### 示例代码 以下是一个使用 `offsetof` 的示例,展示如何计算结构体成员的偏移量: ```c #include <stdio.h> #include <stddef.h> struct Example { char a; // 偏移量 0 int b; // 偏移量 4(假设存在 3 字节对齐填充) double c; // 偏移量 8 }; int main() { printf("Offset of 'a': %zu\n", offsetof(struct Example, a)); // 输出 0 printf("Offset of 'b': %zu\n", offsetof(struct Example, b)); // 输出 4 printf("Offset of 'c': %zu\n", offsetof(struct Example, c)); // 输出 8 return 0; } ``` #### 注意事项 1. **内存对齐**:由于编译器可能会对结构体成员进行内存对齐处理,实际偏移量可能与成员顺序大小不完全一致。例如,在上述示例中,`char a` 后可能有 3 字节的填充以满足 `int b` 的对齐要求[^3]。 2. **安全性**:`offsetof` 不会检查结构体的有效性或成员是否存在,因此如果使用不当可能导致未定义行为[^4]。 3. **跨平台兼容性**:不同平台编译器可能有不同的内存对齐规则,因此在跨平台开发中需特别注意[^5]。 #### 自定义实现 `offsetof` 如果需要手动实现 `offsetof`,可以参考以下代码: ```c #define MY_OFFSETOF(s, m) ((size_t)((char*)&((s*)0)->m - (char*)0)) ``` 此实现与标准定义类似,但显式地计算了地址差值。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值