You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取结构体成员的地址/偏移以实现序列化?

问题描述

我正尝试实现一个既能设置结构体成员,又能将数据序列化输出到外部的函数。为实现序列化,我需要知晓结构体成员的大小和偏移,但在获取偏移时遇到了困难。

在以下代码中,直接打印member的值能得到正确结果,但无法将其转换为整数类型。另外,通过计算成员地址与结构体地址的差值可以得到偏移,但感觉这种方式过于复杂。

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

struct Data {
    int32_t i;
    uint32_t u;
    bool b;
    char c;
};

Data g_data;

template <typename Type>
void set(Type Data::*member, const Type& value)
{
    // 打印正常工作
    printf("Size: %lu\tAddress = %lu\n", sizeof(Type), member);

    // 强制转换报错
    // printf("Size: %lu\tAddress = %lu\n", sizeof(Type), reinterpret_cast<uintptr_t>(member));

    // 可行但过于复杂?
    // printf("Size: %lu\tAddress = %lu\n", sizeof(Type), reinterpret_cast<uintptr_t>(&(g_data.*member)) - reinterpret_cast<uintptr_t>(&g_data));

    g_data.*member = value;
}

int main() {
    set(&Data::i, static_cast<int32_t>(1));
    set(&Data::u, static_cast<uint32_t>(2U));
    set(&Data::b, true);
    set(&Data::c, '1');

    return 0;
}
解决方案

为什么直接转换成员指针会报错

C++中,Type Data::*这类指向成员的指针不是普通内存地址,其底层表示可能包含额外信息(比如处理虚继承的逻辑),因此标准不允许用reinterpret_cast直接将其转换为uintptr_t,编译器会直接报错,这属于未定义行为范畴。

正确获取成员偏移的方式

想要可靠获取结构体成员的偏移量,有两种标准且简洁的方法:

  1. 基于空指针计算偏移
    无需全局结构体实例,直接对空指针进行成员地址计算(仅计算偏移,不实际解引用空指针,符合标准要求):
uintptr_t offset = reinterpret_cast<uintptr_t>(&(reinterpret_cast<Data*>(nullptr)->*member));
  1. 封装通用偏移获取模板
    可以写一个通用模板函数,适配任意结构体的成员指针:
template <typename Struct, typename Member>
size_t get_member_offset(Member Struct::*member) {
    return reinterpret_cast<size_t>(&(reinterpret_cast<Struct*>(nullptr)->*member));
}

使用时直接调用get_member_offset(member)即可得到偏移量。

修改后的代码示例

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

struct Data {
    int32_t i;
    uint32_t u;
    bool b;
    char c;
};

Data g_data;

template <typename Struct, typename Member>
size_t get_member_offset(Member Struct::*member) {
    return reinterpret_cast<size_t>(&(reinterpret_cast<Struct*>(nullptr)->*member));
}

template <typename Type>
void set(Type Data::*member, const Type& value)
{
    size_t offset = get_member_offset(member);
    printf("Size: %zu\tOffset = %zu\n", sizeof(Type), offset);

    g_data.*member = value;
}

int main() {
    set(&Data::i, static_cast<int32_t>(1));
    set(&Data::u, static_cast<uint32_t>(2U));
    set(&Data::b, true);
    set(&Data::c, '1');

    return 0;
}

注意事项

  • 不要依赖直接打印成员指针的结果,这是编译器扩展行为,不同编译器输出可能不一致,标准未规定成员指针的打印格式。
  • 如果使用offsetof宏(来自<cstddef>),要求结构体是标准布局类型;而空指针计算方式无此限制,但需保证成员指针有效。

内容的提问来源于stack exchange,提问作者Armandas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:03:08