关于`&((struct mystruct *)0)->u.b`代码片段的技术问询
问题描述
我编写了如下代码:
struct mystruct { char a; union { char a[8]; char b[16]; } u; }; void fuu(void) { struct mystruct s; printf("%ld %ld\n", sizeof(s), &((struct mystruct *)0)->u.b); }
其中&((struct mystruct *)0)->u.b这段代码令我困惑。我的理解是:首先将0转换为struct mystruct类型的指针(这一步无法理解),然后取出该指针指向的联合体成员u.b(我认为这是char数组b的起始指针),再取该指针的地址并打印。我最困惑的是将0转换为结构体指针这一步,能否详细解释这段代码的执行逻辑?
代码执行逻辑详解
这段代码是计算结构体成员u.b相对于结构体起始地址偏移量的经典C语言技巧,核心就是靠0地址转指针的操作实现,一步步拆解:
1. (struct mystruct *)0:把0转为结构体指针
这不是真的要访问地址0的内存(那样会直接触发崩溃),只是利用C语言的指针语义,让编译器把这个指针当成指向struct mystruct类型数据的"虚拟起始地址"——我们假定这个结构体的起始位置在地址0,目的是让编译器按照结构体的内存布局来计算成员的位置。整个过程是编译期完成的,不会产生实际的内存访问操作。
2. ((struct mystruct *)0)->u.b:定位到u.b的起始位置
编译器根据结构体的内存布局,算出从"虚拟起始地址0"到u.b数组第一个元素的位置偏移。这里注意:数组名u.b在表达式中会自动转为指向数组首元素的指针,也就是等价于&u.b[0]。
3. &((struct mystruct *)0)->u.b:得到偏移量的数值
这里看似是取指针的地址,但实际上,因为前面的指针是从地址0偏移而来的,这个"地址值"就是u.b相对于结构体起始地址的偏移量。编译器会直接把这个数值作为常量计算出来,运行时只是把它当作long类型打印,完全不会碰内存。
补充说明
- 为什么不会崩溃?因为这段代码是编译期常量表达式,编译时就已经算出具体数值,运行时只是打印这个数值,没有任何内存访问行为。
- 你可以用标准库的
offsetof宏替代这段代码,效果完全一致:#include <stddef.h> // ... printf("%ld %ld\n", sizeof(s), offsetof(struct mystruct, u.b));offsetof宏的底层实现本质上就是你写的这段逻辑,只是做了更严谨的兼容性封装。
内容的提问来源于stack exchange,提问作者Moses
相关产品推荐
相关产品推荐

