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




malloc array (8)

Tout d'abord : Une suggestion: S'il vous plaît lire à propos des 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 Il s'agit d'une violation de contrainte, nécessitant un diagnostic à partir de toute implémentation C conforme, conformément au chapitre §6.7.9 / P2:

    Aucun initialiseur ne doit tenter de fournir une valeur pour un objet non contenu dans l'entité initialisée.

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

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

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

    Cependant, un (ny) compilateur avec une conformité stricte activée doit refuser de compiler le code. Dans le cas où le compilateur a choisi de compiler et de produire de toute façon un binaire, le comportement ne va pas avec la portée de la définition du langage C, il va jusqu'à l'implémentation du compilateur (et peut donc varier considérablement).

    Dans ce cas, le compilateur a décidé que cette instruction ne ferait que fonctionnellement le même que char*str= "what";

    Donc, ici, str est un pointeur sur un caractère, qui pointe sur un littéral de chaîne . Vous pouvez ré-assigner au pointeur,

    str="newstring";  //this is valid
    

    mais une déclaration comme

     str[i]="newstring";
    

    serait invalide , car ici, un type de pointeur est tenté d'être converti et stocké dans un type de caractère, où les types ne sont pas compatibles. Le compilateur doit lancer un avertissement sur la conversion non valide dans ce cas.

    Par la suite, une déclaration comme

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

    est syntaxiquement invalide, car vous utilisez l'opérateur de souscription Array [] sur quelque chose qui n'est pas "pointeur sur le type d'objet complet", comme

    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 des éléments de tableau individuels,

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

    mais vous ne pouvez pas affecter à 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 pour les tableaux tient. Cela fait de str un tableau de pointeurs. Ainsi, les membres du tableau, sont assignables, donc,

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

    est parfaitement correct Cependant, les pointeurs, qui sont stockés en tant que membres du tableau, sont des pointeurs vers un littéral de chaîne . Pour la même raison que celle mentionnée ci-dessus, vous appelez un comportement indéfini lorsque vous souhaitez modifier l'un des éléments de la chaîne.

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

Cas 1: Quand j'écris

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

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

Cas 2: Quand j'écris

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

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


cas 1 :

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

Tout d'abord, l'instruction ci-dessus n'est pas valide , lisez les avertissements correctement. str est un simple pointeur, il peut pointer vers single tableau de caractères single à la fois et non vers multiple tableaux de caractères.

bounty.c: 3: 2: avertissement: éléments en excès dans l'initialiseur scalaire [activé par défaut]

str est un char pointer et il est stocké dans la section section de la RAM mais son contents est stocké dans le code(Can't modify the content section code(Can't modify the content de la RAM car str est initialisé avec la string(in GCC/linux) .

comme vous l'avez dit str [i] = "newstring"; est valide alors que str [i] [j] = 'j'; est invalide.

str= "new string" ne provoque pas de modification du code / de la section en lecture seule , ici vous attribuez simplement une new address à str qui explique pourquoi elle est valide mais

*str='j' ou str[0][0]='j' n'est pas valide car vous modifiez ici la section lecture seule en essayant de changer la première lettre de str .

Cas 2:

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

ici str est 2D tableau 2D c'est-à-dire str et str[0],str[1],str[2] lui-même sont stockés dans la stack section de la RAM qui signifie que vous pouvez changer le contenu de chaque str[i] .

str[i][j]='w'; c'est valable parce que vous essayez d'empiler le contenu de la section qui est possible. mais

str[i]= "new string"; ce n'est pas possible car str[0] lui-même un tableau et un tableau est const pointeur (ne peut pas changer l'adresse) , vous ne pouvez pas attribuer une nouvelle adresse.

Simplement dans le premier cas, str="new string" est valid car str est un pointer , pas un array et dans un second cas, str[0]="new string" n'est not valid car str est un array non un pointer .

J'espère que ça aide.


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 mémoire, str est un tableau de pointeurs vers les chaînes individuelles. Habituellement, ces chaînes individuelles doivent résider dans un stockage statique et il est erroné d'essayer de les modifier. Dans le graphique, j'ai utilisé 0 pour désigner les octets nuls finaux.

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

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

Dans ce cas, str est un tableau 2D contigu de caractères situé sur la pile. Les chaînes sont copiées dans cette zone de mémoire lorsque le tableau est initialisé et les chaînes individuelles sont complétées avec zéro octet pour donner au tableau une forme régulière.

Ces deux configurations de mémoire sont fondamentalement incompatibles. Vous ne pouvez pas passer non plus à une fonction qui attend un pointeur sur l'autre. Cependant, l'accès aux chaînes individuelles est compatible. Lorsque vous écrivez str[1] , vous obtenez un caractère char* au premier caractère d'une région mémoire contenant les octets is0 , c'est-à-dire une chaîne 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 tableau-pointeur-décroissance: str[1] désigne en réalité un tableau de cinq octets exactement ( is000 ), qui se désintègre immédiatement en pointeur vers son premier élément dans presque tous les contextes. Cependant, je pense qu'une explication complète du pointeur-décroissance du tableau dépasse le cadre de cette réponse. Google array-pointer-decay si vous êtes curieux.


Cas 1:

Quand j'écris

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

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

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

Dans cette instruction, str est un pointeur sur le type de caractère. Lors de la compilation, vous devez recevoir un message d'avertissement sur cette déclaration:

warning: excess elements in scalar initializer
        char*str={"what","is","this"};
                         ^

Raison de l'avertissement: vous fournissez plusieurs initialiseurs à un scalaire.
[ Les types arithmétiques et les types de pointeurs sont collectivement appelés types scalaires. ]

str est un scalaire et de C Standards # 6.7.9p11 :

L'initialiseur pour un scalaire doit être une expression unique, éventuellement entre accolades. ..

De plus, l'attribution de plusieurs initialiseurs à un scalaire est un comportement indéfini .
De C Standards # J.2 Comportement non défini :

L'initialiseur d'un scalaire n'est ni une expression unique ni une expression unique entre accolades

Comme il s’agit d’un comportement indéfini selon la norme, il est inutile d’en discuter davantage . Discuter de la partie I.II et de la partie I.III avec une hypothèse - char *str="somestring" , juste pour mieux comprendre le type char * .
Il semblerait que vous souhaitiez créer un tableau de pointeurs sur une chaîne. J'ai ajouté un résumé sur le tableau de pointeurs à la chaîne, ci-dessous dans cet article, après avoir parlé des deux cas.

Partie I.II
>> then str[i]="newstring"; is valid

Non , ce n'est pas valide .
Encore une fois, le compilateur doit donner un message d'avertissement sur cette instruction en raison d'une conversion incompatible.
Puisque str est un pointeur sur le type de caractère. Par conséquent, str[i] est un caractère situé à la place de l'objet pointé par str [ str[i] --> *(str + i) ].

"newstring" est un littéral de chaîne et un littéral de chaîne se désintègre en un pointeur, sauf lorsqu'il est utilisé pour initialiser un tableau, de type char * et vous essayez de l'assigner à un type de caractère. D'où le compilateur le signalant comme un avertissement.

Partie I.III
>> whereas str[i][j]='j'; is invalid.

Oui, c'est invalide
L'opérateur [] (souscripteur) peut être utilisé avec des opérandes de type tableau ou pointeur.
str[i] est un caractère et str[i][j] signifie que vous utilisez [] sur un opérande de caractère non valide. D'où le compilateur le signalant comme une erreur.

Cas 2:

Quand j'écris

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

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

Partie II.I
>> char str[][5]={"what","is","this"};

Ceci est absolument correct. Ici, str est un tableau 2D. Basé sur le nombre d'initialiseurs, le compilateur définit automatiquement la première dimension. La vue en mémoire de str[][5] , dans ce cas, serait quelque chose comme ceci:

         str
         +-+-+-+-+-+
  str[0] |w|h|a|t|0|
         +-+-+-+-+-+
  str[1] |i|s|0|0|0|
         +-+-+-+-+-+
  str[2] |t|h|i|s|0|
         +-+-+-+-+-+

Basé sur la liste d'initialisation, les éléments respectifs de 2D-array seront initialisés et le reste des éléments sera mis à 0 .

Partie II.II
>> then str[i]="newstring"; is not valid

Oui, ce n'est pas valide.
str[i] est un tableau à une dimension.
Selon les normes C, un tableau n'est pas une lvalue modifiable.
From C Standards # 6.3.2.1p1 :

Une lvalue est une expression (avec un type d'objet autre que vide) qui désigne potentiellement un objet; 64) si une lvalue ne désigne pas un objet lorsqu'elle est évaluée, le comportement est indéfini. Lorsqu'un objet est dit avoir un type particulier, le type est spécifié par la lvalue utilisée pour désigner l'objet. Une lvalue modifiable est une lvalue qui n’a pas de type de tableau, n’a pas un type incomplet, n’a pas de type qualifié et, s’il s’agit d’une structure ou d’une union, n’a aucun membre (y compris, récursivement, tout membre) ou élément de tous les agrégats ou unions contenus) avec un type qualifié.

En outre, un nom de tableau est converti en pointeur qui pointe vers l'élément initial de l'objet tableau, sauf lorsqu'il s'agit de l'opérande de l'opérateur sizeof, de l'opérateur _Alignof ou de l'opérateur unaire &.

From C Standards # 6.3.2.1p3 :

Sauf s'il s'agit de l'opérande de l'opérateur sizeof, de l'opérateur _Alignof ou de l'opérateur unary &, ou est une chaîne utilisée pour initialiser un tableau, une expression qui a le type '' array of type '' est convertie en une expression avec tapez '' pointer to type '' qui pointe sur l'élément initial de l'objet tableau et n'est pas une lvalue.

Puisque str est déjà initialisé et que vous affectez un autre littéral de chaîne au tableau de str , la chaîne littérale se convertit en un pointeur qui rend l’affectation incompatible car vous avez lvalue de type char tableau et rvalue de type char * . D'où le compilateur le signalant comme une erreur.

Partie II.III
>> whereas str[i][j]='J'; is valid.

Oui, ceci est valide tant que les valeurs i et j sont valides pour le tableau str donné.

str[i][j] est de type char , vous pouvez donc lui affecter un caractère. Attention, C ne vérifie pas les limites des tableaux et l'accès à un tableau hors limites est un comportement indéfini qui inclut - il peut fort bien faire exactement ce que le programmeur a prévu ou une erreur de segmentation ou générer silencieusement des résultats incorrects ou tout peut arriver.

En supposant que dans le cas 1 , vous souhaitez créer un tableau de pointeurs sur chaîne.
Ça devrait être comme ça:

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

La vue en mémoire de str sera quelque chose comme ceci:

      str
        +----+    +-+-+-+-+--+
  str[0]|    |--->|w|h|a|t|\0|
        |    |    +-+-+-+-+--+
        +----+    +-+-+--+
  str[1]|    |--->|i|s|\0|
        |    |    +-+-+--+
        +----+    +-+-+-+-+--+
  str[2]|    |--->|t|h|i|s|\0|
        |    |    +-+-+-+-+--+
        +----+

"what" , "is" et "this" sont des littéraux de chaîne.
str[0] , str[1] et str[2] sont des pointeurs vers le littéral de chaîne respectif et vous pouvez également les faire pointer vers une autre chaîne.

Donc, c'est parfaitement bien:

str[i]="newstring"; 

En supposant que i soit 1, le pointeur str[1] pointe maintenant vers le littéral de chaîne "newstring" :

        +----+    +-+-+-+-+-+-+-+-+-+--+
  str[1]|    |--->|n|e|w|s|t|r|i|n|g|\0|
        |    |    +-+-+-+-+-+-+-+-+-+--+
        +----+

Mais vous ne devriez pas faire ceci :

str[i][j]='j';

(en supposant que i=1 et j=0 , donc str[i][j] est le premier caractère de la deuxième chaîne)

Conformément à la norme tentant de modifier un littéral de chaîne, cela entraîne un comportement indéfini car ils peuvent être stockés dans un stockage en lecture seule ou combinés avec d'autres littéraux de chaîne.

De la norme C # 6.4.5p7 :

Il n'est pas spécifié si ces tableaux sont distincts, à condition que leurs éléments aient les valeurs appropriées. Si le programme tente de modifier un tel tableau, le comportement est indéfini.

Additionnel:

Il n'y a pas de type de chaîne natif en langage C. En langage C, une chaîne est un tableau de caractères terminé par un caractère nul . Vous devez connaître la différence entre les tableaux et les pointeurs.

Je vous suggère de lire ci-dessous pour mieux comprendre les tableaux, les pointeurs et l'initialisation des tableaux:

  1. Initialisation du tableau, vérifiez this .
  2. Équivalence des pointeurs et des tableaux, vérifiez this et this .

Avec le premier, vous définissez une variable qui est un pointeur sur un caractère, qui est généralement utilisé comme une simple chaîne . Il initialise le pointeur pour pointer sur le littéral de chaîne "what" . Le compilateur doit également se plaindre d'avoir trop d'initialiseurs dans la liste.

La seconde définition fait de str un tableau de trois tableaux de cinq caractères. C'est un tableau de trois chaînes de cinq caractères.

Un peu différemment, on peut voir quelque chose comme ceci:

Pour le premier cas:

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

Et pour la seconde tu as

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

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

Cette affectation est également invalide dans la deuxième version, mais pour une autre raison: str[i] est un tableau (de cinq éléments char ) et vous ne pouvez pas l’affecter à un tableau, mais seulement y copier. Donc, vous pouvez essayer de faire strcpy(str[i], "newstring") et le compilateur ne se plaindra pas. C'est faux, cependant, car vous essayez de copier 10 caractères (rappelez-vous le terminateur) dans un tableau de 5 caractères, et cela écrira des limites conduisant à un comportement indéfini .


Juste parce que vous avez dit que d'autres réponses me déroutent, voyons ce qui se passe avec un premier exemple plus simple

char *ptr = "somestring";

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

D'où cnosider 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 (attribuer 1 pointeur à un autre), mais l'instruction 2 n'est pas une opération valide (en essayant d'écrire 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 requis par "somestring" (lecture seule) et c'est tout.

Par conséquent, considérez 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 (l'affectation du nom du tableau à un pointeur, le nom du tableau renvoie l'adresse du 1er octet), l'instruction 2 est également valide car la mémoire n'est pas en lecture seule.

Statement 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 de 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 suit.


  • Pour commencer

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

    n'est même pas valide code C 1) , donc en discuter n'est pas très significatif . Pour une raison quelconque, le compilateur gcc laisse passer ce code avec seulement un avertissement. N'ignorez pas les avertissements du compilateur. Lorsque vous utilisez gcc, assurez-vous de toujours compiler en utilisant -std=c11 -pedantic-errors -Wall -Wextra .

  • Ce que gcc semble faire en rencontrant ce code non standard, c'est de le traiter comme si vous aviez écrit char*str={"what"}; . Qui à son tour est la même chose que char*str="what"; . Ceci n'est nullement garanti par le langage C.

  • str[i][j] essaie indirectement d'indiquer deux fois un pointeur, même s'il ne possède qu'un seul niveau d'indirection, et que vous obtenez donc une erreur de compilation. Cela n'a pas autant de sens que de taper

    int array [3] = {1,2,3}; int x = array[0][0]; .

  • En ce qui concerne la différence entre char* str = ... et char str[] = ... , voir la FAQ: Quelle est la différence entre les caractères s [] et char * s? .

  • En ce qui concerne le char str[][5]={"what","is","this"}; Dans ce cas, il crée un tableau de tableaux (tableau 2D). La dimension la plus interne est définie sur 5 et la dimension la plus externe est définie automatiquement par le compilateur en fonction du nombre d'initialiseurs fournis par le programmeur. Dans ce cas 3, le code est donc équivalent à char[3][5] .

  • str[i] vous donne le numéro de tableau i dans le tableau des tableaux. Vous ne pouvez pas affecter de tableaux dans C, car c'est ainsi que le langage est conçu. De plus, il serait incorrect de le faire pour une chaîne, FAQ: Comment attribuer correctement une nouvelle valeur de chaîne?

1) Il s’agit d’une violation de contrainte de C11 6.7.9 / 2. Voir également 6.7.9 / 11.


  • Dans la première déclaration

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

    déclare str un pointeur sur un caractère et est un scalaire. La norme dit que

    6.7.9 Initialisation (p11):

    L'initialiseur pour un scalaire doit être une expression unique, éventuellement entre accolades . [...]

    Cela dit, un type scalaire peut avoir un initialiseur fermé mais avec une seule expression, mais en cas de

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

    C'est à ce sujet que les compilateurs doivent gérer cela. Notez que ce qui arrive aux autres initialiseurs est un bug . Un vérificateur de confirmation doit donner un message de diagnostic.

    [Warning] excess elements in scalar initializer   
    

    5.1.1.3 Diagnostic (P1):

    Une implémentation conforme doit produire au moins un message de diagnostic (identifié de manière définie par l'implémentation) si une unité de traduction ou une unité de traduction de prétraitement enfreint une règle ou une contrainte de syntaxe, même si elle est explicitement définie comme non définie ou implémentée. défini

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

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

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

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

    Dans ce cas, str[i] est de type char * et un littéral de chaîne peut lui être assigné, mais modifier la chaîne littérale str[i] points pour invoquer un comportement indéfini. Cela dit, vous ne pouvez pas faire str[0][0] = 'W' .

  • L'extrait de code

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

    déclarez str comme un tableau de tableaux de caractères. str[i] est en fait un tableau et comme les tableaux sont des lvalues ​​non modifiables, vous ne pouvez donc pas les utiliser comme opérande gauche de l'opérateur d'affectation. Cela fait que str[i]="newstring"; invalide. While str[i][j]='J'; fonctionne parce que les éléments d'un tableau peuvent être modifiés.





arrayofarrays