1、前言
在C语言的结构体中,由于字节对齐的问题,所以成员的地址并不能直接根据数据类型的大小进行计算,使用宏offsetof可以获得结构体成员相对于结构体首地址的字节偏移量。
2、offsetof宏实现
在C标准库中offsetof宏的声明如下,需要包含头文件stddef.h:
size_t offsetof(type, member);
参数:
type:结构体类型
member:结构体中成员的名称
返回值:
返回类型为size_t的值,表示type中member的偏移量
该宏在Linux内核源码内的实现如下:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
通过上面这个宏为什么能获取到结构体某个成员的偏移地址呢?首先,偏移地址的计算方法为:当前的成员地址-结构体首地址=偏移地址,在offsetof宏中,将0地址强制转换成了(TYPE *)类型,也就是说结构体的首地址为0,所以根据偏移地址计算方法,可以知道当前成员的地址也就等于偏移地址,所以offsetof宏将成员的地址取出并返回,获得的就是某个成员的偏移地址了,返回值为size_d,需要引起注意。
3、实例
下面是一个offsetof的使用实例,代码如下所示:
#include <stdio.h> #include <stddef.h> #include <stdlib.h> struct s { int i; char c; double d; char a[]; }; int main(int argc, char *argv[]) { printf("sizeof(struct s)=%zd\n", sizeof(struct s)); printf("offsets: i=%zd; c=%zd; d=%zd; a=%zd.\n", offsetof(struct s, i), offsetof(struct s, c), offsetof(struct s, d), offsetof(struct s, a)); exit(EXIT_SUCCESS); }
使用gcc编译后运行结果如下所示:
由于字节对齐的原因,所以结构体s的大小为16个字节,假设首地址从0开始,由于int类型占用4字节,所以地址0到3为i所用,char类型占用1字节,地址4为c所用,由于需要字节对齐并且double类型的开始地址必须是8的整数倍,因此地址5到7被填充,double类型占用8字节,地址8到15被d所用,a的地址则从地址16开始,因此,通过宏offsetof便可以得到结构体成员的偏移地址。