Ignorer les problèmes de remplissage/d'alignement et étant donné la structure suivante, quelle est la meilleure façon d'obtenir et de définir la valeur de member_b sans utiliser le nom du membre.
struct mystruct {
int member_a;
int member_b;
}
struct mystruct *s = malloc(sizeof(struct mystruct));
En d'autres termes; Comment exprimeriez-vous ce qui suit en termes de pointeurs/décalages:
s->member_b = 3;
printf("%i",s->member_b);
Ma conjecture est de
int
)char
?)int
et définissez l'adresse (sur *charpointer + offset
?)int
pointeur pour définir le contenu de la mémoiremais je suis un peu confus au sujet de la conversion en un type de caractère ou si quelque chose comme memset
est plus approprié ou si généralement je suis en train de préconiser cela totalement faux.
Bravo pour toute aide
L'approche que vous avez décrite est à peu près correcte, bien que vous devriez utiliser offsetof
au lieu d'essayer de déterminer le décalage par vous-même. Je ne sais pas pourquoi vous mentionnez memset
- il définit le contenu d'un bloc à une valeur spécifiée, ce qui semble tout à fait indépendant de la question posée.
Voici du code pour montrer comment cela fonctionne:
#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;
}
La technique complète:
Obtenez le décalage en utilisant offsetof:
b_offset = offsetof (struct mystruct, membre_b);
Obtenez l'adresse de votre structure en tant que pointeur char *.
char * sc = (char *) s;
Ajoutez l'ajout de l'offset à l'adresse de la structure, convertissez la valeur en un pointeur sur le type et la déréférence appropriés:
* (int *) (sc + décalage_b)
Ignorer le rembourrage et l'alignement, comme vous l'avez dit ...
Si les éléments vers lesquels vous pointez sont entièrement d'un seul type, comme dans votre exemple, vous pouvez simplement convertir la structure en le type souhaité et la traiter comme un tableau:
printf("%i", ((int *)(&s))[1]);
Il est possible de calculer le décalage en fonction de la structure et de NULL comme pointeur de référence, par exemple "& (((type *) 0) -> champ)"
Exemple:
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;
}
Dans cet exemple particulier, vous pouvez l'adresser par *((int *) ((char *) s + sizeof(int)))
. Je ne sais pas pourquoi vous voulez cela, donc je suppose des fins didactiques, donc l'explication suit.
Le morceau de code se traduit par: prenez la mémoire à partir de l'adresse s et traitez-la comme de la mémoire pointant vers char
. À cette adresse, ajoutez sizeof(int)
char
- morceaux - vous obtiendrez une nouvelle adresse. Prenez la valeur que l'adresse ainsi créée et traitez-la comme un int
.
Notez que l'écriture de *(s + sizeof(int))
donnerait l'adresse à s
plus sizeof(int)
sizeof (mystruct) chunks
Modifier: selon le commentaire d'Andrey, en utilisant offsetof :
*((int *) ((byte *) s + offsetof(struct mystruct, member_b)))
Edit 2: j'ai remplacé tous les byte
s par char
s car sizeof(char)
est garanti d'être 1.
Il ressort de vos commentaires que ce que vous faites réellement est de compresser et de décompresser un tas de types de données disparates dans un seul bloc de mémoire. Alors que vous pouvez vous en sortir avec des lancers de pointeurs directs, comme la plupart des autres réponses l'ont suggéré:
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);
}
Le problème est que ce n'est pas portable. Il n'y a aucun moyen général de garantir que les types sont stockés dans votre bloc avec l'alignement correct, et certaines architectures ne peuvent tout simplement pas effectuer des chargements ou des stockages vers des adresses non alignées. Dans le cas particulier où la disposition de votre bloc est définie par une structure déclarée, ce sera OK, car la disposition de la structure inclura le remplissage nécessaire pour assurer le bon alignement. Cependant, comme vous ne pouvez pas accéder aux membres par leur nom, il semble que ce ne soit pas vraiment ce que vous faites.
Pour ce faire, vous devez utiliser 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;
}
(similaire pour les autres types).