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