Un tableau de char* dans une structure ?

a marqué ce sujet comme résolu.

Bonjour,

Pour un petit projet perso, j'ai besoin de la structure menu_c comme définie ci-dessous :

1
2
3
4
5
typedef struct menu_struct {
    char* choices[5] = {"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5"};
    int choices_nb;
    int starty, startx, height, width;
} menu_c;

Seulement, à la compilation, je me mange une erreur pas vraiment explicite :

1
2
3
menu.c:17:19: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘=’ token
  char* choices[5] = {"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5"};
                   ^

Après quelques recherches, je comprends bien qu'il y a un problème entre l'allocation de la mémoire de la structure et l'allocation pour le tableau de chaînes, mais je n'arrive pas à trouver une solution pour me sortir de ce bourbier… :(

J'ai pensé à utiliser un char** et à initialiser la liste ailleurs, mais ça m'a l'air d'une fausse bonne idée, si vous voyez ce que je veux dire…

Salut,

Tu est en train de définir un type, un type n'a pas de valeur, vouloir affecter une valeur lors de la création d'un type n'a pas de sens, d'où l'erreur sur le égal. Tu pourras affecter des valeurs quand tu créeras des variables de type menu_c

Tu as l'aire de connaitre le nombre de choix à l'avance. Du coup "char* tab[5]" c'est bien.

Si tu ne connais pas le nombre de choix … char** tab c'est mieux.

+0 -0

Cette méthode ne marche que pour les tableaux statiques. Il me semble que sizeof résonne presque tout le temps en type. Et donc puisque tous les pointeurs font la même taille tu obtiens 1.

Du coup soit tu définies une valeur comme la fin du tableau soit tu utilises une variable à part qui stocke la taille du tableau.

+0 -0

Salut,

Ah les pointeurs, toute une histoire.

Il y a une petit subtilité en C avec les pointeurs et les tableaux. Avec char* choices[5]; tu déclares pour le compilateur un pointeur vers un tableau de 5 caractères, soit un pointeur vers une chaine de 5 caractères de long. Or ce que tu semble vouloir, c'est un tableau de 5 chaines de caractères.

Ce qu'il te faut c'est donc un tableau de pointeurs, qui ce code comme suit en C :

1
char (*choices)[5];

Comme ça tu pourras déclarer tout de suite la valeur de chaque case :

1
char (*choices)[5] = {"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5"};
+0 -0

Salut,

Avec char* choices[5]; tu déclares pour le compilateur un pointeur vers un tableau de 5 caractères […]

Delyas

Non, là tu déclares un tableau de cinq pointeurs sur char.

Ce qu'il te faut c'est donc un tableau de pointeurs, qui ce code comme suit en C :

1
char (*choices)[5];

Comme ça tu pourras déclarer tout de suite la valeur de chaque case :

1
char (*choices)[5] = {"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5"};

Delyas

Le code que tu présentes est faux. D'une part, tu utilises une initialisation multiple alors que tu n'initialises pas un agrégat et, d'autre part, tu essayes d'assigner un pointeur sur char à un pointeur sur un tableau de cinq char. En effet, une chaîne de caractère littérale (comme "Choice 1") est un tableau de char, mais celui-ci est converti en un pointeur sur son premier élément.

Il me semble que sizeof résonne presque tout le temps en type.

ache

Pourquoi presque ? ;)

+0 -0

@Taurre: Oh c'est pas très important … sizeof avec les LVA ne va pas se contenter d'évaluer le type de l'expression mais va aussi lancer l'expression à l'execution. Donc elle retourne toujours la taille d'un type mais maintenant sizeof fait un peu plus. Bref, "presque" juste pour introduire cette nuance …

+0 -0

@Taurre: Oh c'est pas très important … sizeof avec les LVA ne va pas se contenter d'évaluer le type de l'expression mais va aussi lancer l'expression à l'execution.

ache

En fait, dans le cas des VLA, c'est juste que le type ne peut être déterminé qu'à l'exécution. Pour autant, sizeof raisonne toujours bien sur le type de l'expression. ;)

+0 -0

Je crois vous vous êtes perdu non ?

Sauf erreur de ma part, il te donne une erreur parce que tu fais un tableaux de pointeur de char :

1
char* [5]choices;

Sauf que tu n'initialise pas avec des pointeurs, mais avec des tableaux ! Regarde la différence

1
2
3
4
5
6
7
char toto1 = 'a';
char toto2 = 'a';
char toto3 = 'a';
char toto4 = 'a';
char toto5 = 'a';

char* choices[5] = {&toto5, &toto4, &toto3, &toto2, &toto1};
1
2
3
4
5
6
7
char toto1[10] = "Choice 1";
char toto2[10] = "Choice 2";
char toto3[10] = "Choice 3";
char toto4[10] = "Choice 4";
char toto5[10] = "Choice 5";

char[10] choices[5] = {toto5, toto4, toto3, toto2, toto1};

Je ne suis pas sûr que tu puisse faire la déclaration ainsi mais c'est pour t'expliquer.

En réalité ce que tu mets dans ton tableau choices, ce sont des tableaux de caractères, pas "juste" des pointeurs. C'est le "problème" du C, le compilo veut savoir combien de place il te réserve. Or la syntaxe char* t'en réserve 1 et toi tu en demande 9..

Tu as deux solutions qui sont :

  • faire un tableaux à deux dimensions (ce que tu as essayé de faire)

  • de l'allocation dynamique en passant par une fonction

+0 -0

T'as essayé ton code dans son cas d'usage ? C'est à dire une initialisation lors de la déclaration du type. Je maintiens que ça n'a aucun sens en C. En C++, c'est autre chose, puisque les structures sont des classes et ont des constructeurs, on peut donner une sémantique à cette syntaxe. Mais là on est en C, il n'y a pas de constructeur, donc pas de valeur d'initialisation pour des types.

En revanche, la syntaxe char* choices[5] = {"Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5"}; est parfaitement valide: on déclare un tableau de 5 char* initialisé avec les adresses de chaines littérales. Les chaines ne sont pas dans le tableau, le tableau est un tableau de pointeurs.

PS: Pour rappel:

postfix-expression:
postfix-expression [ expression ]

unary-expression:
unary-operator cast-expression

unary-operator: one of
& * + - ~ !

cast-expression:
unary-expression

+2 -0

Dans ce cas, on peut déclarer une chaine de caractère ainsi :

1
char* toto = "tata et titi";

Si c'est le cas je viens d'apprendre un truc, mais on m'a toujours dit que ce n'était pas possible.

En revanche

1
char toto[] = {'t', 'a', 't', 'a',/*...*/ 'i'};

Est juste. Donc j'avoue que tu m'as mis le doute. Je n'ai jamais essayer cette façon de faire puisqu'on m'a dit de pas le faire :)

Edit : Effectivement, ici il est écrit que c'est possible, j'aurais appris un truc !

+0 -0

Dans ce cas, on peut déclarer une chaine de caractère ainsi :

1
char* toto = "tata et titi";

Encore mieux :

1
char * const toto = "tata et titi". 

Ainsi, dès que tu tentera de modifier ta chaîne, le compilateur t'enverra balader. C'est un très bon garde-fou, parce que modifier une chaîne de caractère déclarée ainsi est un comportement indéterminé (si mes souvenirs sont bons) et amène bien souvent un segfault.

En revanche

1
char* toto = {'t', 'a', 't', 'a',/*...*/ 'i'};

Est juste. […].

Ricocotam

Heu… Non, c'est faux : là tu assignes une constante entière (la valeur de 't') à ton pointeur toto. Cela revient exactement au même que si tu avais écrit :

1
char *p = 't';
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte