Un tableau de char* dans une structure ?

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

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…

Cette réponse a aidé l'auteur du sujet

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

+1 -0

Bonsoir,

Juste pour info, mis à part l'erreur (tolérée) de const-correctness sur les chaines de caractères, ce code est parfaitement valide en C++ (voir méthode d'initialisation des membres non-static introduit par C++11).

+0 -0

Elle est légale en C++11, mais comme je ne savais pas si le C11 avait introduit cette possibilité, j'ai préféré préciser. Mais on est HS là.

Édité par gbdivers

+0 -0

Cette réponse a aidé l'auteur du sujet

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.

Édité par ache

ache.one                                                                                   🦊

+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.

Édité par ache

ache.one                                                                                   🦊

+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"};

Édité par Delyas

+0 -0

C'est d'ailleurs pour cela qu'il est en général conseillé d'écrire char *ptr plutot que char* ptr, ca permet aussi d'eviter les erreur du genre

1
char* ptr1, ptr2;

Ou ptr est bien un pointeur, mais ptr2 est un simple char

+0 -0
Staff

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 ? ;)

Édité par Taurre

+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 …

ache.one                                                                                   🦊

+0 -0
Staff

@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

Édité par Ricocotam

+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

Édité par Natalya

+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 !

Édité par Ricocotam

+0 -0
Staff

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.

Staff

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';

Édité par Taurre

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

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