stddef offsetof

offsetof 说明

在c标准的stddef.h中有一个获取c/c++结构或者类成员偏移量的宏offsetof,具体定义如下:

 #ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
#else
#define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
#endif

#else

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

参数说明:

s: 结构体类型

m: 是结构体数据成员名称

思想就是将地址0强制转换成类型s,然后通过求结构体数据成员地址的方式,获取偏移量。

C程序的例子:


#include <stdio.h>
struct A{
    int a1; 
    int a2; 
};
main()
{
    struct A a, *pa;
    a.a1=22;
    a.a2=33;
    pa = &a; 

    // Print the address of the data member of variable a
    printf("By variable a &a(%p) &a.a1(%p) &a.a2(%p) \n", &a, &a.a1, &a.a2);

    // Print the address of the data member of the pointer pa. The pa point to the variable a.
    printf("By pointer pa &a(%p) &pa->a1(0x%lx) &pa->a2(0x%lx)\n", &a, (size_t)&pa->a1, (size_t)&pa->a2);

    struct A *s=(struct A*)0;
    // Print the address of the data member of the pointer s. The s point to 0.
    printf("By pointer s &s->a1(%ld) &s->a2(%ld)\n", (size_t)&s->a1, (size_t)&s->a2);

    // Change s point to 100.
    s = (struct A*)100;
    printf("By pointer s &s->a1(%ld) &s->a2(%ld)\n", (size_t)&s->a1, (size_t)&s->a2);

    // First deference the pointer s, then get the address of the data memeber. 
    printf("By pointer s &(*s).a1(%ld) &(*s).a2(%ld)\n", (size_t)&(*s).a1, (size_t)&(*s).a2);
    // Dangerous? Deference the pointer s, the get value of the data member.
    // printf("By pointer s (*s).a1(%d) (*s).a2(%d)\n", (*s).a1, (*s).a2);
}

执行结果:

By variable a &a(0x7fff5e0bae60) &a.a1(0x7fff5e0bae60) &a.a2(0x7fff5e0bae64)

By pointer pa &a(0x7fff5e0bae60) &pa->a1(0x7fff5e0bae60) &pa->a2(0x7fff5e0bae64)

By pointer s &s->a1(0) &s->a2(4)

By pointer s &s->a1(100) &s->a2(104)

By pointer s &(*s).a1(100) &(*s).a2(104)

从执行结果可以得出这样的结论:

1、对结构体数据成员取地址,是以结构体变量首地址为基础的。

2、获取数据成员的地址运算是指针运算,不管地址是合法的还是非法的,都可以。
因为这只是地址运算,不会操作地址所指向的 内存。

3、如果我们把地址设为0,那么对数据成员取地址,就可以获取偏移量了。

C++例子

我们将上面的例子简单修改一下,增加一个virtual 成员函数,看看效果:

#include <stdio.h>
struct A{
    virtual ~A(){}
    int a1; 
    int a2; 
};
main()
{
    struct A a, *pa;
    a.a1=22;
    a.a2=33;
    pa = &a; 

    // Print the address of the data member of variable a
    printf("By variable a &a(%p) &a.a1(%p) &a.a2(%p) \n", &a, &a.a1, &a.a2);

    // Print the address of the data member of the pointer pa. The pa point to the variable a.
    printf("By pointer pa &a(%p) &pa->a1(0x%lx) &pa->a2(0x%lx)\n", &a, (size_t)&pa->a1, (size_t)&pa->a2);

    struct A *s=(struct A*)0;
    // Print the address of the data member of the pointer s. The s point to 0.
    printf("By pointer s &s->a1(%ld) &s->a2(%ld)\n", (size_t)&s->a1, (size_t)&s->a2);

    // Change s point to 100.
    s = (struct A*)100;
    printf("By pointer s &s->a1(%ld) &s->a2(%ld)\n", (size_t)&s->a1, (size_t)&s->a2);

    // First deference the pointer s, then get the address of the data memeber. 
    printf("By pointer s &(*s).a1(%ld) &(*s).a2(%ld)\n", (size_t)&(*s).a1, (size_t)&(*s).a2);
    // Dangerous? Deference the pointer s, the get value of the data member.
    // printf("By pointer s (*s).a1(%d) (*s).a2(%d)\n", (*s).a1, (*s).a2);
}

执行结果:

By variable a &a(0x7fffd686f520) &a.a1(0x7fffd686f528) &a.a2(0x7fffd686f52c)

By pointer pa &a(0x7fffd686f520) &pa->a1(0x7fffd686f528) &pa->a2(0x7fffd686f52c)

By pointer s &s->a1(8) &s->a2(12)

By pointer s &s->a1(108) &s->a2(112)

By pointer s &(*s).a1(108) &(*s).a2(112)

首个数据成员的偏移量是8,原因是因为有了vptr指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值