2010-04-09 3 views
2

Je veux avoir une fonction qui accepte différents types de structures comme arguments. Donc, comme je n'ai pas de type spécifique, je dois utiliser void *. Maintenant la question est: quand je passe une structure à cette fonction, comment puis-je accéder à un membre connu de cette structure à l'intérieur de la fonction? Plus précisément, je sais que toutes les structures ont str1 en tant que membre et je veux, par exemple, l'imprimer. Voici un exemple de code:Comment accéder à un membre de structure dans une fonction qui l'obtient comme type void *?

struct { 
    char* str1; 
    float tt1; } var1 = {"This is me", 12}; 

struct { 
    char* str1; 
    int tt2; } var2 = {"This is you", 18}; 

void printStruct(void* str) 
{ 
    printf("\n the structure string is %s", ??); 
    //can I put something in ?? to print the string? 
} 

main(....) 
{ 
    printStruct(&var1); 
    printStruct(&var2); 
} 
+0

L'avez-vous essayé? – John

+0

Juste un petit conseil. Vous pourriez vouloir déclarer le char * à un const. Comme ceci: const char * str1. – ant2009

Répondre

5

Vous pouvez jeter le type que vous voulez qu'il soit et accéder au membre, par exemple, supposons que vous avez votre prototype, faites ceci:

struct X 
{ 
char* str1; 
float tt1; } var1 = {"This is me", 12}; 

void printStruct(void* str) 
{ 
    ((struct X *)str)->str1; 
    ... 
} 

Notez l'utilisation de supports; ce qui se passe est un cast de type void * à struct var2 * et ce type, à l'intérieur des parenthèses outermost(), est accessible en tant que variable de ce type.

+0

Je ne pense pas que cela fonctionnera à moins que les structures aient des noms. Dans la question d'origine, "var2" est le nom de la variable, mais pas le nom de la structure. – Skrud

+0

@Skrud yep, désolé édité po –

0

Si vous passez dans un void* je ne vois pas comment la méthode pourrait savoir quoi que ce soit sur ce que vous passez, sauf que c'est une adresse à quelque chose. Ce que cela signifie, c'est que vous devez le lancer avant de pouvoir l'utiliser, ou bien assurez-vous que tous les struct que vous transmettez ont le char* exactement au même endroit à chaque fois.

2

Pour la plupart des compilateurs, il est juste sûr de lancer et d'accéder au membre de cette façon, puisqu'il est le premier membre de vos deux structures, les chances sont que l'alignement sera le même.

La chose est bien, puisque vous aurez besoin de jeter les pointeurs dans une structure dont ils ont besoin d'avoir des balises:

struct S1 { char* str1; float tt1; } var1 = {"This is me", 12}; 

struct S2 { char* str1; int tt2; } var2 = {"This is you", 18}; 

void printStruct(void* str) { printf("\n the structure string is %s", ((struct S1 *)str)->str1); } 

Notez cependant que pour des exemples plus complexes avec plus variables, et un ordre différent, que ce peut-être pas sûr, tout dépend de l'alignement choisi par le compilateur.

2

D'abord, un exemple simple:

int booger(int type, void * st) { 
    switch(type) { 
     case foo: 
     struct foo * f = (struct foo *)st; 
     /// do stuff 
     break; 
     case bar: 
     /// ... 

Maintenant, pour votre struct: définir une nouvelle structure qui ne contient que le premier membre - la chaîne: struct st { char * str; }; Maintenant, vous pouvez passer toutes vos autres structures dans votre fonction en les convertissant en virtual_st *, puis faites la même chose que dans le premier exemple pour obtenir les membres réels.

int baz(struct st * s) { 
    if (!strcmp(s->str, "foo")) { 
     // ... 

EDIT: Par ailleurs, ce qui est similaire à la façon dont C++ compilateurs que la sortie c faire, à l'exception que le champ initial est un pointeur vers une structure du type qui contient des pointeurs de fonction pour les fonctions membres et statique les membres du groupe. Je ne sais pas exactement comment l'héritage multiple fonctionne, cependant.

3

Vous devriez donner les noms de vos structures.Par exemple,

struct foo { 
    char * str1; 
    float tt1; 
}; 

Ensuite, vous pouvez déclarer vos instances comme ceci:

struct foo var1 = { "This is me", 12 }; 

Ensuite, vous pouvez lancer la void * à un struct foo * dans votre fonction:

void printStruct(void * ptr) { 
    printf("str=%s\n", ((struct foo *)ptr)->str1); 
} 

Et vous pouvez appeler comme ceci:

int main(...) { 
    struct foo var1 = { "This is me", 12 }; 
    printStruct(&var1); 
} 

Mise à jour: Commenter Johannes avait raison, ma réponse ne tient pas compte du fait que l'affiche originale disait qu'il pourrait ne pas connaître le type de chaque structure. solution mise à jour est la suivante:

Puisque vous savez que tous vos struct contiennent un char * str1, vous pouvez profiter de « modèle de mémoire plat » de C en jetant vos struct directement à struct générique, étiquette qui ne contient que char * str1. Cela ne fonctionnera que si char * str1 est le premier élément des structures.

struct base { char * str1; }; 
struct { char * str1; float tt1; } var1 = { "This is me", 12 }; 
struct { char * str1, int tt2; } var2 = { "This is me", 18 }; 

void printStruct(void * ptr) { 
    printf("str is %s\n", ((struct base *)ptr)->str1); 
} 

Ceci est un hack assez sale, cependant, IMO. S'il y a d'autres membres que vous voulez partager, vous ne pourrez pas utiliser cette même astuce.

Une autre option consiste à définir une struct qui utilise une union avec un recenseur de garder une trace de quel type est en fait utilisé dans l'union:

enum { FLOAT_TYPE, INT_TYPE }; 
struct foo { 
    char * str1; 
    union { 
     float tt1; 
     int tt2; 
    } numericValue; 
    int unionType; 
}; 

Cela vous permettra de définir des variables comme ceci:

struct foo var1 = { .str1 = "This is me", .numericValue.tt1 =12, .unionType = FLOAT_TYPE }; 
struct foo var2 = { .str1 = "This is me", .numericValue.tt2 =18, .unionType = INT_TYPE }; 

vous n'avez pas besoin de gérer pour différents types de struct, vous pouvez lancer votre pointeur vide à la place struct foo *:

void printStruct(void * ptr) { 
    struct foo * p = (struct foo *)ptr; 
    printf("string is %s\n", p->str1); 
} 

Cela pourrait nécessiter un peu plus de travail, mais c'est beaucoup plus propre, IMO.

+0

Tho le gars a dit que la fonction reçoit différents types de structures. Dans votre code, vous supposez qu'il reçoit seulement 'struct foo'. –

+0

Techniquement parlant 'struct foo var1 = {" Ceci est moi ", 12};' est une définition, pas une déclaration. : p Si le compilateur réserve de l'espace pour une instance ou implémente une fonction, alors il la définit; sinon, s'il ne fait absolument rien d'autre que reconnaître une entité, alors il la déclare. – wilhelmtell

+0

@wilhelmtell, une définition est une déclaration, donc vous vous trompez. –

8

Vous pouvez passer un pointeur de fonction pour découpler la fonction de ses utilisateurs

void printStruct(void* data, char * (*namef)(void *data)) { 
    printf("\n the structure string is %s", namef(data));  
} 

Supposons ensuite passer vous un struct S1* à la fonction, vous pouvez passer un getter comme ceci:

char * whois(void *data) { 
    return ((struct S1*)data)->str1; 
} 

int main(void) { 
    struct S1 s = {"This is me", 12}; 
    printStruct(&s, whois); 
} 

Vous pourriez utiliser une alternative plus laide avec offsetof si

void printStruct(void* data, size_t named) { 
    printf("\n the structure string is %s", *(char**) ((char*)data + named));  
} 

int main(void) { 
    struct S1 s = {"This is me", 12}; 
    printStruct(&s, offsetof(struct S1, str1)); 
} 

offsetof est une macro qui vous donne le décalage d'un membre dans une structure. Dans la fonction, vous positionnez le pointeur sur le membre char* (vous devez donc convertir le contenu en char** pour obtenir le type approprié) et obtenir la valeur du membre.

Cependant, je préférerais la première façon d'utiliser les fonctions.De cette façon, la fonction d'impression ne doit même pas savoir que data pointe vers une structure. Il peut le donner comme une boîte noire à la fonction getter et vous pouvez laisser le compilateur gérer les calculs de décalage de bas niveau pour vous.

+0

Nice. J'espère que je ne suis jamais dans une situation où je dois faire cela. –

Questions connexes