web-dev-qa-db-fra.com

Déclaration et initialisation du pointeur à deux caractères C

J'ai toujours pensé que déclarer

char *c = "line";

était le même que

char c[] = "line";

et donc je l'ai fait

char **choices = { "New Game", "Continue Game", "Exit" };

Ce qui me donne un type de pointeur incompatible, où

char *choices[] = { "New Game", "Continue Game", "Exit" };

non. Une aide pour comprendre cela?

25
vascop

Eh bien, ce n'est pas pareil. Il est juste plus facile pour la plupart des gens de les considérer comme étant les mêmes, donc tout le monde commence à penser de cette façon jusqu'à ce qu'ils rencontrent un problème comme celui-ci :-)

J'allais écrire quelque chose de long et d'essoufflé, mais je me suis dit ... Quelqu'un d'autre a déjà dû faire ça. Et ils l'ont fait. Ceci est une assez bonne explication:

http://www.lysator.liu.se/c/c-faq/c-2.html

La façon la plus simple d'y penser est que lorsque vous faites quelque chose comme:

 char * foo = "quelque chose"; 

Vous faites vraiment quelque chose comme:

 char randomblob [] = "quelque chose"; 
 char * foo = randomblob; 

Maintenant ... ce n'est pas vraiment une image précise (bien que je ne sois pas un expert en compilation). Cela vous permet au moins de penser aux choses d'une manière un peu plus correcte.

Donc, revenons à votre problème, si [~ # ~] je [~ # ~] comprends bien les choses (ce qui n'est jamais garanti), vous pouvez ' t faites votre exemple de ligne # 3 en C. Vous avez raison que quelqu'un puisse écrire un compilateur qui ferait la bonne chose ici, mais pas gcc. Le quatrième exemple, cependant, fait la "bonne chose" et vous donne "un tableau de pointeurs qui pointent chacun vers un tableau de caractères const eux-mêmes".

Une fois, j'ai parcouru une page Web qui traduirait un type C complexe en anglais. C'était probablement au début des années 90, mais je parie que si vous cherchez assez sur Google, cela vous donnera une description de libellé plus précise que celle que je viens de préparer.

13
Wes Hardaker
char *c = "line";

est pas identique à

char c[] = "line";

c'est vraiment la même chose que

static char hidden_C0[] = "line";
char *c = hidden_C0;

sauf que la variable hidden_C0 n'est pas directement accessible. Mais vous le verrez si vous videz le langage d'assemblage généré (il aura généralement un nom qui n'est pas un identificateur C valide, comme .LC0). Et dans votre exemple de tableau de constantes de chaîne, la même chose se passe:

char *choices[] = { "New Game", "Continue Game", "Exit" };

devient

char hidden_C0[] = "New Game";
char hidden_C1[] = "Continue Game";
char hidden_C2[] = "Exit";

char *choices[] = { hidden_C0, hidden_C1, hidden_C2 };

Maintenant, c'est un comportement de cas spécial qui est disponible niquement pour les constantes de chaîne. Tu ne peux pas écrire

int *numbers = { 1, 2, 3 };

tu dois écrire

int numbers[] = { 1, 2, 3 };

et c'est pourquoi tu ne peux pas écrire

char **choices = { "a", "b", "c" };

soit.

(Votre confusion est un cas particulier de l'idée fausse courante selon laquelle les tableaux sont "les mêmes que" les pointeurs en C. Ils ne le sont pas. Les tableaux sont des tableaux. Les variables avec des types de tableau souffrent type decay à un type de pointeur lorsque ils sont tilisés (dans presque tous les contextes), mais pas lorsqu'ils sont définis.)

16
zwol

C'est bon, il suffit d'écrire

char **choices = (char *[]){ "New Game", "Continue Game", "Exit" };

Cependant, choices ne peut être utilisé que pour l'adressage linéaire. Par exemple:

printf ("%s", &(*choices)[0]); sorties: New Game
printf ("%s", &(*choices)[1]); sorties: ew Game
printf ("%s", &(*choices)[9]); sorties: Continue Game

Ce n'est donc pas une blague, c'est une initialisation valide. Juste un autre type d'utilisation.


Vous pouvez également trouver un exemple très proche ici , expliquant les littéraux composés .

5
Martin Babacaev

Norme C en ligne (brouillon n1256 ):

6.7.8 Initialisation
...
11 L'initialiseur d'un scalaire doit être un expression unique, éventuellement enfermé entre accolades. La valeur initiale de l'objet est celle de l'expression (après conversion); les mêmes contraintes de type et conversions que pour l'affectation simple s'appliquent, en prenant le type du scalaire comme la version non qualifiée de son type déclaré.
...
16 Sinon, l'initialiseur d'un objet qui a type d'agrégat ou d'union doit être un accolade jointe liste des initialiseurs pour les éléments ou membres nommés.

Je souligne.

char ** est un type scalaire, pas un agrégat, et n'est donc pas compatible avec l'initialiseur {"New Game", "Continue Game", "Exit"}. Par contre, char *[] est un type agrégé (tableau).

De même, vous ne pouviez pas écrire quelque chose comme

int *foo = {1, 2, 3};

car int * n'est pas un type de tableau.

Votre compréhension de

char *c = "line";

et

char c[] = "line";

est légèrement éteint; ils ne sont pas identiques. Le premier formulaire copie l'adresse du littéral de chaîne dans la valeur de pointeur c. Le second formulaire copie le contenu de l'expression de tableau "line" au tampon désigné par c.

2
John Bode