代码之家  ›  专栏  ›  技术社区  ›  Chris Farmiloe

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

  •  26
  • Chris Farmiloe  · 技术社区  · 16 年前

    忽略填充/对齐问题 给定以下结构,在不使用成员名称的情况下获取和设置成员_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);
    

    我猜是

    • 通过查找成员的大小来计算偏移量( int
    • char ?)
    • int 指针并将地址设置为 *charpointer + offset ?)
    • 设置内存内容的指针

    但是我对转换为char类型或类似的类型感到有点困惑 memset

    为任何帮助干杯

    6 回复  |  直到 16 年前
        1
  •  33
  •   Jerry Coffin    7 年前

    您概述的方法大致正确,但您应该使用 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;
    }
    
        2
  •  28
  •   R Samuel Klatchko    16 年前

    完整技术:

    1. 使用offsetof获取偏移:

      b_offset=offsetof(struct mystruct,member_b);

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

      char*sc=(char*)s;

    3. *(整数*)(sc+b_偏移)

        3
  •  4
  •   mskfisher KeithS    16 年前

    忽略填充和对齐,正如您所说。。。

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

    printf("%i", ((int *)(&s))[1]);
    
        4
  •  3
  •   Obik    11 年前

    &((type*)0)——>(现场) "

    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;
    }
    
        5
  •  1
  •   laura    16 年前

    在这个特定的示例中,您可以通过 *((int *) ((char *) s + sizeof(int))) . 我不知道你为什么要那样做,所以我假设你是出于说教的目的,所以解释如下。

    代码位翻译为:取从地址s开始的内存,并将其视为指向的内存 char . 向该地址添加 sizeof(int) 烧焦 int

    注意写作 *(s + sizeof(int)) 我会在 s 加 sizeof(内部) sizeof(mystruct)块

    offsetof

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

    编辑2:我替换了所有 byte 烧焦 s作为 sizeof(char) 保证为1。

        6
  •  0
  •   caf    16 年前

    正如大多数其他答案所建议的那样,使用直接指针投射可以避免这种情况:

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

    (其他类型类似)。