【问题标题】:How can I get/set a struct member by offset如何通过偏移量获取/设置结构成员
【发布时间】:2011-01-03 20:55:50
【问题描述】:

忽略填充/对齐问题并给出以下结构,在不使用成员名称的情况下获取和设置 member_b 值的最佳方法是什么。

struct mystruct {
    int member_a;
    int member_b;
}
struct mystruct *s = malloc(sizeof(struct mystruct));

换一种说法;您将如何用指针/偏移量表达以下内容:

s->member_b = 3;
printf("%i",s->member_b);

我的猜测是

  • 通过查找 member_a (int) 的大小来计算偏移量
  • 将结构转换为单字指针类型 (char?)
  • 创建一个int 指针并设置地址(到*charpointer + offset?)
  • 使用我的int 指针来设置内存内容

但我对转换为 char 类型感到有些困惑,或者像 memset 这样的东西是否更合适,或者我通常完全错误。

为任何帮助干杯

【问题讨论】:

  • 为什么你想这样做?
  • 我在脚本环境和 C 扩展之间架起了一座桥梁(想想 Python C 类型),我在运行时定义和访问结构。
  • 那你是在生成C代码吗?在这种情况下,您也可以生成 C 代码来访问结构成员。我误会了吗?

标签: c struct


【解决方案1】:

您概述的方法大致正确,但您应该使用offsetof 而不是尝试自己计算偏移量。我不知道你为什么提到memset——它将一个块的内容设置为一个指定的值,这似乎与手头的问题完全无关。

这里有一些代码来演示它是如何工作的:

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

typedef struct x {
    int member_a;
    int member_b;
} x;

int main() { 
    x *s = malloc(sizeof(x));
    char *base;
    size_t offset;
    int *b;

    // initialize both members to known values
    s->member_a = 1;
    s->member_b = 2;

    // get base address
    base = (char *)s;

    // and the offset to member_b
    offset = offsetof(x, member_b);

    // Compute address of member_b
    b = (int *)(base+offset);

    // write to member_b via our pointer
    *b = 10;

    // print out via name, to show it was changed to new value.
    printf("%d\n", s->member_b);
    return 0;
}

【讨论】:

  • 感谢您的回复,memset 只是一个暂时的想法,因为它只是在地址上写入内存的另一种方式,它更像是“不要让我坚持我的方法 - 我是打开以不同的方式做”评论:) 偏移量实际上是我很好的一部分,它是如何实际偏移一个我认为我正在努力解决的 void 指针....是否适合转换我的结构例如指向 char* 的指针?
  • 或:((struct mystruct *)p)-&gt;member_b = 3;
  • 由于内存(错误)对齐,是否需要 (char*) 演员表?
【解决方案2】:

在此特定示例中,您可以通过*((int *) ((char *) s + sizeof(int))) 对其进行寻址。我不知道你为什么要这样,所以我假设是教学目的,因此解释如下。

这段代码翻译为:获取从地址s开始的内存,并将其视为指向char的内存。向该地址添加sizeof(int) char-chunks - 您将获得一个新地址。取地址由此创建的值并将其视为int。

请注意,写入*(s + sizeof(int)) 将给出s 加上sizeof(int) sizeof(mystruct) 块的地址

编辑:根据安德烈的评论,使用offsetof:

*((int *) ((byte *) s + offsetof(struct mystruct, member_b)))

编辑2:我将所有bytes 替换为chars,因为sizeof(char) 保证为1。

【讨论】:

  • 为什么要使用byte 而不是char?此外,((byte *) s + sizeof(int)) 的类型为byte *,与char * 相同,因此取消引用它(可能)不会给您int - 如果sizeof(int) != sizeof(char)。在取消引用之前,您需要对 int * 进行强制转换。
  • 正确,部分 - 你的例子更具说教性。我使用 byte 是因为我觉得它具有更多的说教意义。但是请注意,sizeof(byte) 可能不等于 sizeof(char),具体取决于架构。
  • 不正确。为了访问member_b 字段,您必须添加offsetof(mystruct, member_b),它不一定与sizeof(int) 相同。
  • byte 是什么? C标准没有定义它。另外,如果sizeof(byte) != sizeof(char),那么你的代码中肯定需要char 而不是byte,因为sizeof(char) 是1。
  • AndreyT:我确实允许它忽略填充问题,因此可以认为 sizeof/offset 相同。劳拉:谢谢你为我做的!
【解决方案3】:

如你所说,忽略填充和对齐...

如果您指向的元素完全属于单一类型,如您的示例所示,您可以将结构转换为所需类型并将其视为数组:

printf("%i", ((int *)(&s))[1]);

【讨论】:

  • +1 用于发现数组,并获得一个偷偷摸摸的快捷方式 :) 不幸的是,我的结构可以包含不同的类型
【解决方案4】:

完整的技术:

  1. 使用 offsetof 获取偏移量:

    b_offset = offsetof(struct mystruct, member_b);

  2. 以 char * 指针的形式获取结构的地址。

    char *sc = (char *)s;

  3. 将偏移量添加到结构地址,将值转换为指向适当类型的指针并取消引用:

    *(int *)(sc + b_offset)

【讨论】:

  • +1 表示可移植方法link
【解决方案5】:

从您的 cmets 看来,您真正在做的是将一堆不同的数据类型打包和解包到单个内存块中。虽然您可以通过直接指针转换来避免这样做,正如大多数其他答案所建议的那样:

void set_int(void *block, size_t offset, int val)
{
    char *p = block;

    *(int *)(p + offset) = val;
}

int get_int(void *block, size_t offset)
{
    char *p = block;

    return *(int *)(p + offset);
}

问题在于这是不可移植的。没有通用的方法来确保类型以正确的对齐方式存储在您的块中,并且某些架构根本无法对未对齐的地址进行加载或存储。在块的布局由声明的结构定义的特殊情况下,这是可以的,因为结构布局将包含必要的填充以确保正确对齐。但是,由于您无法通过名称访问成员,因此听起来这实际上并不是您在做的事情。

要便携地执行此操作,您需要使用memcpy:

void set_int(void *block, size_t offset, int val)
{
    char *p = block;

    memcpy(p + offset, &val, sizeof val);
}

int get_int(void *block, size_t offset)
{
    char *p = block;
    int val;

    memcpy(&val, p + offset, sizeof val);
    return val;
}

(其他类型类似)。

【讨论】:

    【解决方案6】:

    可以根据结构体和NULL作为引用指针计算偏移量 例如“&(((type *)0)->field)”

    例子:

    struct my_struct {
        int x;
        int y;
        int *m;
        int *h;
    };
    int main()
    {
        printf("offset %d\n", (int) &((((struct my_struct*)0)->h)));
        return 0;
    }
    

    【讨论】:

    • 我对 C 语言比较陌生,所以我觉得这很神奇,但它是如何工作的呢?为什么你可以从那个强制转换的空指针访问 h?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-15
    相关资源
    最近更新 更多