Подтвердить что ты не робот

Как я могу получить/установить член структуры смещением

Игнорирование проблем заполнения/выравнивания и задание следующей структуры, лучший способ получить и установить значение 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);

Я предполагаю, что

  • вычислить смещение, найдя sizeof member_a (int)
  • отбрасывает структуру в один тип указателя слова (char?)
  • создать указатель int и установить адрес (на *charpointer + offset?)
  • используйте указатель int для установки содержимого памяти

но я немного запутался в том, что вы выбрали тип char, или если что-то вроде memset более подходящее, или, вообще говоря, я прихожу к этому совершенно неправильно.

Приветствия за любую помощь

4b9b3361

Ответ 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;
}

Ответ 2

Полная техника:

  • Получить смещение с помощью offsetof:

    b_offset = offsetof (struct mystruct, member_b);

  • Получите адрес вашей структуры как указатель char *.

    char * sc = (char *) s;

  • Добавить добавление смещения к адресу структуры, приведение значения к указателю на соответствующий тип и разыменование:

    * (int *) (sc + b_offset)

Ответ 3

Игнорирование отступов и выравнивания, как вы сказали...

Если элементы, на которые вы указываете, целиком состоят из одного типа, как в вашем примере, вы можете просто перевести структуру в нужный тип и рассматривать ее как массив:

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

Ответ 4

Это можно вычислить смещение на основе структуры и 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;
}

Ответ 5

В этом конкретном примере вы можете указать его *((int *) ((char *) s + sizeof(int))). Я не уверен, почему вы этого хотите, поэтому я принимаю дидактические цели, поэтому объяснение следует.

Бит кода преобразуется как: возьмите память, начиная с адреса s, и обработайте ее как память, указывающую на char. На этот адрес добавьте sizeof(int) char -chunks - вы получите новый адрес. Возьмите значение, созданное таким образом, и рассмотрите его как int.

Обратите внимание, что запись *(s + sizeof(int)) даст адрес в s плюс sizeof(int) sizeof (mystruct) chunks

Изменить: согласно комментарию Андрея, используя offsetof:

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

Изменить 2: я заменил все byte на char, поскольку sizeof(char) гарантированно будет 1.

Ответ 6

Из ваших комментариев видно, что то, что вы действительно делаете, - это упаковка и распаковка кучи разрозненных типов данных в один блок памяти. Хотя вы можете уйти от этого с помощью прямых указателей, поскольку большинство других ответов предложили:

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

(аналогично для других типов).