30 votes

Quelle est la différence entre les définitions de tableaux char*str={"foo",...} et char str[][5]={"foo",...} ?

Cas 1 : Quand j'écris

char*str={"what","is","this"};

puis str[i]="newstring"; est valide alors que str[i][j]='j'; est invalide.

Cas 2 : Quand j'écris

char str[][5]={"what","is","this"};

puis str[i]="newstring"; n'est pas valide alors que str[i][j]='J'; est valable.

Pourquoi est-ce le cas ? Je suis un débutant qui est déjà très confus après avoir lu les autres réponses.

5 votes

Peut-être que vous voulez dire char * str[] = {"what", "is", "this"}; pour le premier ?

0 votes

Essayez char *str[] = {"what","is","this"}; putchar(str[0][0]); d'abord et char *str[] = {"what","is","this"}; str[0][0] = 't'; suivant. Tu peux apprendre une autre chose. Voir stackoverflow.com/questions/8795576/ après l'expérience.

1 votes

@ajay brahmakshatriya oui

26voto

Sourav Ghosh Points 54713

Tout d'abord : Une suggestion : Lisez la rubrique les tableaux ne sont pas des pointeurs et vice-versa ! !

Cela dit, pour éclairer ce scénario particulier,

  • Dans le premier cas ,

    char*str={"what","is","this"};

    ne fait pas ce que vous pensez qu'il fait. Il s'agit d'une violation de contrainte, nécessitant un diagnostic de la part de toute implémentation C conforme, conformément au chapitre§6.7.9/P2 :

    Aucun initialisateur ne doit tenter de fournir une valeur pour un objet qui n'est pas contenu dans l'entité qui est initialisée.

    Si vous activez les avertissements, vous ( au moins ) voir

    avertissement : éléments en excès dans l'initialisateur scalaire

      char*str={"what","is","this"};

    Cependant, un ou plusieurs compilateurs dont la conformité stricte est activée devraient refuser de compiler le code. Si le compilateur choisit quand même de compiler et de produire un binaire, le comportement n'entre pas dans le cadre de la définition du langage C, il dépend de l'implémentation du compilateur (et peut donc varier considérablement).

    Dans ce cas, le compilateur a décidé de rendre cette instruction fonctionnellement identique à celle de char*str= "what";

    Donc, ici str est un pointeur vers un char qui indique une littéral de chaîne de caractères . Vous pouvez réaffecter au pointeur,

    str="newstring";  //this is valid

    mais, une déclaration comme

     str[i]="newstring";

    serait invalide comme ici, on tente de convertir un type de pointeur et de le stocker dans un fichier de type char où les types ne sont pas compatibles. Le compilateur devrait lancer un avertissement sur la conversion invalide dans ce cas.

    Par la suite, une déclaration comme

    str[i][j]='J'; // compiler error

    n'est pas syntaxiquement valide, car vous utilisez l'indice Array. [] sur quelque chose qui n'est pas un "pointeur vers un type d'objet complet", comme par exemple

    str[i][j] = ...
          ^^^------------------- cannot use this
    ^^^^^^ --------------------- str[i] is of type 'char', 
                                 not a pointer to be used as the operand for [] operator.
  • En revanche, dans le second cas ,

    str est un tableau de tableaux. Vous pouvez modifier les éléments individuels du tableau,

     str[i][j]='J'; // change individual element, good to go.

    mais vous ne pouvez pas assigner à un tableau.

     str[i]="newstring";  // nopes, array type is not an lvalue!!

  • Enfin, considérant que vous vouliez écrire (comme vu dans les commentaires)

    char* str[ ] ={"what","is","this"};

    dans votre premier cas, la même logique s'applique aux tableaux. Cela fait str un tableau de pointeurs. Donc, les membres du tableau, sont assignables, donc,

    str[i]="newstring";  // just overwrites the previous pointer

    est parfaitement acceptable. Cependant, les pointeurs, qui sont stockés en tant que membres du tableau, sont des pointeurs vers les éléments suivants littéral de chaîne de caractères donc pour la même raison que celle mentionnée ci-dessus, vous invoquez comportement indéfini lorsque l'on veut modifier un des éléments de la mémoire appartenant à la chaîne littérale.

     str[i][j]='j';   //still invalid, as above.

1 votes

La première partie de votre réponse n'est pas correcte. Si str a un type char * alors str[i] est un char lvalue, pas un pointeur. Si vous lui attribuez une chaîne littérale, vous obtiendrez au moins un avertissement au moment de la compilation. L'assignation fait un entier à partir d'un pointeur sans cast. . Et ensuite une erreur de segmentation au moment de l'exécution pour avoir essayé d'écrire dans une mémoire constante.

4 votes

En effet char*str={"what","is","this"}; est une violation de contrainte, nécessitant un diagnostic de toute implémentation C conforme. N1570 6.7.9p2 : "Aucun initialisateur ne doit tenter de fournir une valeur pour un objet non contenu dans l'entité en cours d'initialisation." (Il est IMHO malheureux que gcc traite ceci comme un avertissement non fatal par défaut).

16voto

cmaster Points 7460

La disposition de la mémoire est différente :

char* str[] = {"what", "is", "this"};

    str
+--------+      +-----+
| pointer| ---> |what0|
+--------+      +-----+   +---+
| pointer| -------------> |is0|
+--------+                +---+    +-----+
| pointer| ----------------------> |this0|
+--------+                         +-----+

Dans cette disposition de la mémoire, str est un tableau de pointeurs vers les chaînes de caractères individuelles. Habituellement, ces chaînes individuelles résident dans un stockage statique, et c'est une erreur d'essayer de les modifier. Dans le graphique, j'ai utilisé 0 pour désigner les octets nuls de fin.

char str[][5] = {"what", "is", "this"};

  str
+-----+
|what0|
+-----+
|is000|
+-----+
|this0|
+-----+

Dans ce cas, str est un tableau contigu de caractères en 2D situé sur la pile. Les chaînes de caractères sont copiées dans cette zone de mémoire lors de l'initialisation du tableau, et les chaînes de caractères individuelles sont complétées par des octets zéro pour donner au tableau une forme régulière.

Ces deux dispositions de la mémoire sont fondamentalement incompatibles l'une avec l'autre. Vous ne pouvez pas passer l'une ou l'autre à une fonction qui attend un pointeur vers l'autre. Cependant, l'accès aux chaînes de caractères individuelles est compatible. Lorsque vous écrivez str[1] vous obtenez un char* au premier caractère d'une région de mémoire contenant les octets is0 c'est-à-dire une chaîne de caractères C.

Dans le premier cas, il est clair que ce pointeur est simplement chargé depuis la mémoire. Dans le second cas, le pointeur est créé via array-pointer-decay : str[1] désigne en fait un tableau d'exactement cinq octets ( is000 ), qui se transforme immédiatement en un pointeur vers son premier élément dans presque tous les contextes. Cependant, je pense qu'une explication complète de la désintégration des pointeurs de tableau dépasse le cadre de cette réponse. Google array-pointer-decay si vous êtes curieux.

3voto

Joachim Pileborg Points 121221

Avec la première, vous définissez une variable qui est un pointeur vers un fichier char qui est généralement utilisé comme un simple une seule chaîne . Il initialise le pointeur pour qu'il pointe sur le littéral de la chaîne de caractères. "what" . Le compilateur debe se plaint également que vous avez trop d'initialisateurs dans la liste.

La deuxième définition fait str un tableau de trois tableaux de cinq char . C'est-à-dire qu'il s'agit d'un tableau de trois chaînes de cinq caractères.


Un peu différemment, cela peut être vu comme ceci :

Pour le premier cas :

+-----+     +--------+
| str | --> | "what" |
+-----+     +--------+

Et pour le second, vous avez

+--------+--------+--------+
| "what" | "is"   | "this" |
+--------+--------+--------+

Notez également que pour la première version, avec le pointeur vers une seule chaîne, l'expression str[i] = "newstring" devrait également conduire à des avertissements, car vous essayez d'assigner un pointeur à l'élément simple char élément str[i] .

Cette affectation est également invalide dans la deuxième version, mais pour une autre raison : str[i] est un tableau (de cinq char ) et vous ne pouvez pas assigner à un tableau, seulement le copier. Vous pourriez donc essayez en faisant strcpy(str[i], "newstring") et le compilateur ne se plaindra pas. C'est mauvais parce que vous essayez de copier 10 caractères (n'oubliez pas le terminateur) dans un tableau de 5 caractères, ce qui entraînera une écriture hors limites et le résultat suivant comportement indéfini .

0 votes

Un pointeur vers un char n'est pas "une simple chaîne". Il peut s'agir d'un pointeur vers une chaîne, mais une chaîne est par définition une séquence de caractères, et non un pointeur.

2voto

haccks Points 33022
  • Dans la première déclaration

    char *str={"what","is","this"}; 

    déclare str un pointeur vers un char et est un scalaire. La norme dit que

    6.7.9 Initialisation (p11) :

    L'initialisateur d'un scalaire doit être un expression simple, éventuellement entourée d'accolades . [...]

    Cela dit, un type scalaire peut avoir un initialisateur entre accolades mais avec une seule expression, mais dans le cas de

    char *str = {"what","is","this"}; // three expressions in brace enclosed initializer

    c'est aux compilateurs de décider comment ils vont gérer cela. Notez que ce qui se passe pour le reste des initialisateurs est une bogue . Un compilateur de confirmation devrait donner un message de diagnostic.

    [Warning] excess elements in scalar initializer   

    5.1.1.3 Diagnostics (P1) :

    Une implémentation conforme doit produire au moins un message de diagnostic (identifié d'une manière définie par l'implémentation) si une unité de traduction de prétraitement ou une unité de traduction contient une violation de toute règle ou contrainte syntaxique, même si le comportement est aussi explicitement spécifié comme non défini ou défini par l'implémentation.

  • Vous prétendez " str[i]="newstring"; est valide alors que str[i][j]='j'; est invalide. "

    str[i] est de char et ne peut contenir qu'un char type de données. Affectation de "newstring" (qui est de char * ) n'est pas valide. La déclaration str[i][j]='j'; n'est pas valide car l'opérateur d'indice ne peut être appliqué qu'à un tableau ou à un type de données pointeur.

  • Vous pouvez faire str[i]="newstring"; en déclarant str comme un tableau de char *

    char *str[] = {"what","is","this"};

    Dans ce cas str[i] est de char * et un littéral de chaîne de caractères peut lui être assigné mais la modification du littéral de chaîne de caractères str[i] vers lequel il pointe va invoquer un comportement non défini. Cela dit, vous ne pouvez pas faire str[0][0] = 'W' .

  • L'extrait

    char str[][5]={"what","is","this"};

    déclarer str comme un tableau de tableaux de char s. str[i] est en fait un tableau et comme les tableaux sont des valeurs l non modifiables, vous ne pouvez pas les utiliser comme opérande gauche d'un opérateur d'affectation. Cela rend str[i]="newstring"; invalide. Alors que str[i][j]='J'; fonctionne parce que les éléments d'un tableau peuvent être modifiés.

1voto

Raman Points 1568

Ce n'est pas parce que vous avez dit que d'autres réponses me déroutent, voyons d'abord ce qui se passe avec un exemple plus simple.

char *ptr = "somestring";

Ici "somestring" est un littéral de chaîne de caractères qui est stocké dans section de données en lecture seule de la mémoire. ptr est un pointeur (alloué comme les autres variables dans la même section de code) qui pointe sur le premier octet de cette mémoire allouée.

Considérez donc ces deux déclarations

char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a';     //statement 2 error

L'instruction 1 effectue une opération parfaitement valide (affectation d'un pointeur à un autre), mais l'instruction 2 n'est pas une opération valide (tentative d'écriture dans un emplacement en lecture seule).

Par contre, si on écrit :

char ptr[] = "somestring";

Ici, ptr n'est pas réellement un pointeur, mais le nom d'un tableau (contrairement au pointeur, il ne prend pas d'espace supplémentaire dans la mémoire). Il alloue le même nombre d'octets que celui requis par la fonction "somestring" (pas en lecture seule) et c'est tout.

Considérons donc les deux mêmes déclarations et une déclaration supplémentaire

char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a';     //statement 2 OK
ptr = "someotherstring" //statement 3 error

L'instruction 1 effectue une opération parfaitement valide (affectation du nom du tableau à un pointeur, le nom du tableau renvoie l'adresse du premier octet), l'instruction 2 est également valide car la mémoire n'est pas en lecture seule.

La déclaration 3 n'est pas une opération valide car ici ptr n'est pas un pointeur, il ne peut pas pointer vers un autre emplacement mémoire.


Maintenant, dans ce code,

char **str={"what","is","this"};

*str est un pointeur ( str[i] est identique à *(str+i) )

mais dans ce code

char str[][] = {"what", "is", "this"};

str[i] n'est pas un pointeur. C'est le nom d'un tableau.

La même chose que ci-dessus se produit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X