如何通过偏移获取/设置结构成员

时间:2010-01-11 18:21:06

标签: c struct

忽略填充/对齐问题并给出以下结构,在不使用成员名称的情况下获取和设置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这样的东西更合适或者通常我正在处理这个完全错误而感到有些困惑。

为任何帮助干杯

6 个答案:

答案 0 :(得分:32)

您概述的方法大致正确,但您应该使用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;
}

答案 1 :(得分:26)

完整的技术:

  1. 使用offsetof:

    获取偏移量
      

    b_offset = offsetof(struct mystruct,member_b);

  2. 将结构的地址作为char *指针。

      

    char * sc =(char *)s;

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

      

    *(int *)(sc + b_offset)

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:3)

可以根据struct和NULL计算偏移量作为参考指针 例如&#34; &amp;(((type *)0) - &gt; field)&#34;

示例:

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;
}

答案 4 :(得分:1)

在此特定示例中,您可以*((int *) ((char *) s + sizeof(int)))解决此问题。我不确定你为什么要这样,所以我假设有教学目的,因此解释如下。

代码位转换为:从地址s开始占用内存并将其视为指向char的内存。到该地址,添加sizeof(int) char - 块 - 您将获得一个新地址。获取由此创建的地址的值,并将其视为int

请注意,撰写*(s + sizeof(int))会提供ssizeof(int) sizeof(mystruct)块的地址

编辑:根据Andrey的评论,使用offsetof

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

编辑2:我将所有byte替换为char s,sizeof(char)保证为1.

答案 5 :(得分:0)

从您的评论中可以看出,您真正在做的是将一堆不同的数据类型打包并解压缩到一个内存块中。虽然你可以通过直接指针转换来做到这一点,但正如大多数其他答案所暗示的那样:

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);
}

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

为了便于移植,您需要使用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;
}

(与其他类型相似)。