offsetof宏的实现
题目:写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
注:这个题目主要是在考察offsetof宏的实现
1.offsetof函数的定义
#include <stddef.h>
size_t offsetof(type, member);
但请注意,实际上offsetof是一个宏,而不是一个函数
它接受两个参数:一个结构体类型和一个该类型中的成员名称,并返回该成员在结构体中的字节偏移量
使用举例:
#include <stddef.h>
#include<stdio.h>
struct S
{double a;int b;char c;
};int main()
{printf("S 结构中的 a 偏移 = %d 字节。\n", (int)offsetof(struct S,a));//0printf("S 结构中的 b 偏移 = %d 字节。\n", (int)offsetof(struct S,b));//8printf("S 结构中的 c 偏移 = %d 字节。\n", (int)offsetof(struct S,c));//12
}
2.offsetof的模拟实现
我们假设结构体起始地址是0,则其成员的地址取出来再强制类型转换为int便可以表示结构体中某个成员相对于起始位置的偏移量
StructType是结构体类型名,MemberName是成员名。具体操作方法是:
- 先将0转换为一个结构体类型的指针,相当于某个结构体的首地址是0。此时,每一个成员的偏移量就成了相对0的偏移量,这样就不需要减去首地址了。这只是一个计算技巧,我们并没有真正去访问地址0的内存(否则会导致程序崩溃)
- 对该指针用->访问其成员,并取出地址,由于结构体起始地址为0,此时成员偏移量直接相当于对0的偏移量,所以得到的值直接就是对首地址的偏移量
- 取出该成员的地址,强转成size_t并打印,就求出了这个偏移量
#define OFFSETOF(StructType, MemberName) (size_t)&(((StructType *)0)->MemberName)// (StructType *)0 *用于将数字0强制转换成一个指向StructType结构体类型的指针
struct StructType
{int a;char c;double d;
};
int main()
{printf("%d\n", OFFSETOF(struct StructType, a));printf("%d\n", OFFSETOF(struct StructType, c));printf("%d\n", OFFSETOF(struct StructType, d));return 0;
}
注意:这里有一个关键点在于,C编译器在编译时就知道结构体内每个成员的精确偏移量,而OFFSEROF宏只是利用编译器的这个知识来获取偏移量数值,并不真正访问内存
这个过程不会影响真实内存
因为这个过程完全发生再编译阶段,而不是运行时:
4. 编译器在编译时就知道结构体的内存布局
5. 当看到这个表达式时,编译器直接计算偏移量数值
6. 最终生成的代码中不包含任何实际访问地址0的操作
7. 结果是一个编译时常量,直接嵌入到最终的可执行文件中
类比理解,可以把其想象成:
8. 你有一张建筑蓝图(结构体定义)
9. 你想知道某个房间距离大楼入口有多远(成员偏移量)
10. 你说:“假设大楼入口在坐标0点,那么房间的坐标就是它距离入口的偏移量”
11. 你不需要真正建造这栋大楼就能计算出这个距离