programa - ¿Cómo puedo obtener/establecer un miembro de estructura por compensación?
punteros en c (6)
Ignorando los problemas de relleno / alineación y dada la siguiente estructura, ¿cuál es la mejor manera de obtener y establecer el valor de member_b sin usar el nombre del miembro?
struct mystruct {
int member_a;
int member_b;
}
struct mystruct *s = malloc(sizeof(struct mystruct));
Dicho de otra manera; ¿Cómo expresaría lo siguiente en términos de indicadores / compensaciones?
s->member_b = 3;
printf("%i",s->member_b);
Mi conjetura es
- calcule el desplazamiento encontrando el tamaño del miembro_a (
int
) - lanzar la estructura a un solo tipo de puntero de palabra (
char
?) - crear un puntero
int
y establecer la dirección (a*charpointer + offset
?) - uso mi puntero
int
para establecer los contenidos de la memoria
pero me confundo un poco acerca de la conversión a un tipo de char o si algo como memset
es más apropiado o si en general estoy de acuerdo con esto totalmente equivocado.
Saludos por cualquier ayuda
El enfoque que ha descrito es más o menos correcto, aunque debería usar offsetof
lugar de intentar calcular el desplazamiento por su cuenta. No estoy seguro de por qué mencionas memset
: establece el contenido de un bloque a un valor específico, que parece no tener relación con la pregunta en cuestión.
Editar: código de demostración:
#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;
}
En este ejemplo en particular, puede abordarlo por *((int *) ((char *) s + sizeof(int)))
. No estoy seguro de por qué quieres eso, así que estoy asumiendo fines didácticos, por lo tanto, la explicación sigue.
El bit de código se traduce como: toma la memoria que comienza en la dirección s y la trata como memoria apuntando a char
. A esa dirección, agregue sizeof(int)
char
-chunks - obtendrá una nueva dirección. Tome el valor que la dirección así creada y trátelo como un int
.
Tenga en cuenta que escribir *(s + sizeof(int))
daría la dirección en s
plus sizeof(int)
sizeof (mystruct) chunks
Editar: según el comentario de Andrey, usando offsetof :
*((int *) ((byte *) s + offsetof(struct mystruct, member_b)))
Edición 2: Reemplacé todos los byte
con caracteres, ya que sizeof(char)
está garantizado que es 1.
Es posible calcular el desplazamiento basado en la estructura y NULL como puntero de referencia, por ejemplo, " & (((tipo *) 0) -> campo) "
Ejemplo:
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;
}
Ignorando el relleno y la alineación, como dijiste ...
Si los elementos a los que apunta son de un solo tipo, como en su ejemplo, puede simplemente convertir la estructura al tipo deseado y tratarla como una matriz:
printf("%i", ((int *)(&s))[1]);
La técnica completa:
Obtenga el desplazamiento utilizando offsetof:
b_offset = offsetof (struct mystruct, member_b);
Obtenga la dirección de su estructura como un puntero char *.
char * sc = (char *) s;
Agregue el agregar el desplazamiento a la dirección de la estructura, eche el valor a un puntero al tipo apropiado y la desreferencia:
* (int *) (sc + b_offset)
Por sus comentarios, suena que lo que realmente está haciendo es empaquetar y desempacar un montón de tipos de datos dispares en un solo bloque de memoria. Si bien puedes salirte con la tuya con los moldes de punteros directos, como la mayoría de las otras respuestas sugirieron:
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);
}
El problema es que esto no es portátil. No hay una forma general de garantizar que los tipos se almacenen dentro de su bloque con la alineación correcta, y algunas arquitecturas simplemente no pueden hacer cargas o almacenar en direcciones desalineadas. En el caso especial donde el diseño de su bloque está definido por una estructura declarada, estará bien, ya que el diseño de la estructura incluirá el relleno necesario para garantizar la alineación correcta. Sin embargo, como no puede acceder a los miembros por su nombre, parece que esto no es lo que está haciendo.
Para hacer esto de forma portátil, debes usar 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;
}
(similar para los otros tipos).