offsetof 详解

本文详细解释了offsetof宏的定义与用途,特别关注如何在不实际访问结构体成员的情况下,计算该成员在结构体中的偏移量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

offsetof 宏

在 <stddef.h> 中定义了个 offsetof(s,m)宏,这个宏用来取得结构体中元素的偏移量很方便,下面是此宏的具体定义:
#define offsetof(s, m) (size_t)&(((s *)0)->m)

ofssetof(s, m) 其中,s 是结构体名,m 是它的一个成员。s 和 m 同是宏  offsetof() 的形参,这个宏返回的是结构体 s 的成员 m 在结构体中的偏移地址。

(s *)0  :  这里的用法实际上是欺骗了编译器,使编译器认为 "0" 就是一个指向 s 结构体的指针(地址),还句话说 s 结构体就是位于 0x0 这个地址处。

(s *)0-> m : 自然就是指向这个结构体的 m 元素。

&((s *)0)->m :  表示 m 元素的地址。这里,如上面所说,因为编译器认为结构体 s 被认为是处于 0x0 地址处,所以 m 的地址自然的就是 m 在 s 中的偏移地址了。

最后将这个偏移值转化为 size_t 类型。

可能会感到迷惑,这样强制转换后的结构指针怎么可以用来访问结构体字段?呵呵,其实这个表达式根本没有也不打算访问m字段。ANSIC标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s*的NULL指针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。

size_t是针对系统定制的一种数据类型。它不是固定位数,在不同的系统里这个值都有可能不同( 它实际上是 unsigned int 类型 );而且在内存里,对于数据是高位对齐存储还是低位对齐存储各系统都不一样。所以,为了提高代码的可移植性,就有必要定议这样的数据类型。一般这种类型都会定义到它具体占几位内存等。当然,有些是编译器或系统已经给定义好的。具体要查看技手册。

 
10月29日
欺骗编译器的#define offsetof(s,m) (size_t)&(((s *)0)->m)
  在MPC8323上使用到SEC,在文件sec2.h的L58中,涉及这样的语句:
#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif
  琢磨半天也比较头疼那个0的含义,感觉就是转化一个0为s结构,获取成员m的地址。但是,为啥是0呢?为啥呢?
 
  搜索了才发现,原来这个东西也很巧妙。
  (s   *)0   是骗编译器说有一个指向类(或结构)s的指针,其值0。
  &(((s   *)0)->m)   是要取得类s中成员变量m的地址,由于这个类的基址为0,这时m的地址当然就是m在s中的偏移了。
 
  代码巧妙的地方就在于用0来表示这个指针,0成为了这个类/结构的基址,所以成员的地址就成了偏移地址。
  或许这也是代码精巧所在吧!
引用\[1\]:C语言字节对齐问题详解中提到了C语言中的字节对齐问题。在结构体中,为了提高内存访问的效率,编译器会对结构体进行字节对齐。这意味着结构体的成员在内存中并不是紧凑排列的,而是按照一定的规则进行对齐。具体的对齐规则取决于编译器和编译选项。\[1\] 引用\[2\]:在C语言中,可以使用宏offsetof来获取结构体成员相对于结构体开头的字节偏移量。这个宏非常有用,可以帮助我们计算出每个结构体成员相对于结构体开头的偏移字节数。通过这个宏,我们可以更好地理解结构体的内存布局。\[2\] 引用\[3\]:在C语言中,指针和结构体的组合常常用于处理复杂的数据结构。指针可以指向结构体的成员,通过指针可以方便地对结构体进行操作。指针和结构体的组合可以实现更灵活的数据处理和内存管理。\[3\] 综上所述,C语言中的指针结构体组合可以用于处理复杂的数据结构,而字节对齐问题则是在结构体中为了提高内存访问效率而进行的优化。通过使用宏offsetof,我们可以更好地理解结构体的内存布局。 #### 引用[.reference_title] - *1* *3* [结构体指针,C语言结构体指针详解](https://blog.youkuaiyun.com/weixin_34069265/article/details/117110735)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言之结构体详解](https://blog.youkuaiyun.com/m0_70749276/article/details/127061692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值