malloc array




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

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 .

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.


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 .


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.


Pour éviter la confusion, vous devez bien comprendre les pointeurs, les tableaux et les initialiseurs. Une idée fausse commune parmi les débutants en programmation C est qu’un tableau est équivalent à un pointeur.

Un tableau est une collection d'éléments du même type. considérez la déclaration suivante:

char arr[10];

Ce tableau contient 10 éléments, chacun de type char .

Une liste d'initialisation peut être utilisée pour initialiser un tableau de manière pratique. Ce qui suit initialise les éléments du tableau avec les valeurs correspondantes de la liste d'initialisation:

char array[10] = {'a','b','c','d','e','f','g','h','i','\0'};

Les tableaux ne sont pas assignables, l'utilisation de la liste d'initialisation est donc valide uniquement sur la déclaration du tableau.

char array[10];
array = {'a','b','c','d','e','f','g','h','i','\0'}; // Invalid...
char array1[10];
char array2[10] = {'a','b','c','d','e','f','g','h','i','\0'};
array1 = array2; // Invalid...; You cannot copy array2 to array1 in this manner.

Après la déclaration d'un tableau, les affectations aux membres du groupe doivent être effectuées via l'opérateur d'indexation du tableau ou son équivalent.

char array[10];
array[0] = 'a';
array[1] = 'b';
.
.
.
array[9] = 'i';
array[10] = '\0';

Les boucles sont un moyen courant et pratique d'attribuer des valeurs aux membres du groupe:

char array[10];
int index = 0;
for(char val = 'a'; val <= 'i'; val++) {
    array[index] = val;
    index++;
}
array[index] = '\0';

char tableaux de caractères peuvent être initialisés via des littéraux de chaîne qui sont des tableaux de caractères constants à null null:

char array[10] = "abcdefghi";

Cependant, ce qui suit n'est pas valide:

char array[10];
array = "abcdefghi"; // As mentioned before, arrays are not assignable

Passons maintenant aux pointeurs ... Les pointeurs sont des variables pouvant stocker l'adresse d'une autre variable, généralement du même type.

Considérez la déclaration suivante:

char *ptr;

Cela déclare une variable de type char * , un pointeur de caractère. C'est-à-dire un pointeur qui peut pointer vers une variable de caractère.

Contrairement aux tableaux, les pointeurs sont assignables. Ainsi, ce qui suit est valide:

char var;
char *ptr;
ptr = &var; // Perfectly Valid...

Comme un pointeur n'est pas un tableau, une seule valeur peut être attribuée à un pointeur.

char var;
char *ptr = &var; // The address of the variable `var` is stored as a value of the pointer `ptr`

Rappelez-vous qu'un pointeur doit se voir attribuer une seule valeur. Par conséquent, ce qui suit n’est pas valide, car le nombre d’initialiseurs est supérieur à un:

char *ptr = {'a','b','c','d','\0'};

Ceci est une violation de contrainte, mais votre compilateur peut simplement assigner 'a' à ptr et ignorer le reste. Mais même dans ce cas, le compilateur vous avertira car les littéraux de caractères tels que 'a' ont un type int par défaut et sont incompatibles avec le type de ptr qui est char * .

Si ce pointeur a été déréférencé à l'exécution, il en résultera une erreur d'exécution pour l'accès à la mémoire non valide, entraînant le blocage du programme.

Dans votre exemple:

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

à nouveau, il s'agit d'une violation de contrainte, mais votre compilateur peut assigner la chaîne à str et ignorer le reste, et afficher simplement un avertissement:

warning: excess elements in scalar initializer .

Maintenant, voici comment nous éliminons la confusion concernant les pointeurs et les tableaux: Dans certains contextes, un tableau peut se transformer en un pointeur vers le premier élément du tableau. Ainsi, ce qui suit est valide:

char arr[10];
char *ptr = arr;

En utilisant le nom du tableau arr dans une expression d'affectation comme rvalue , le tableau se désintègre en un pointeur vers son premier élément, ce qui rend l'expression précédente équivalente à:

char *ptr = &arr[0];

Rappelez-vous que arr[0] est de type char et que &arr[0] est son adresse de type char * , compatible avec la variable ptr .

Rappelez-vous que les littéraux de chaîne sont des tableaux de caractères constants à terminaison nulle, donc l'expression suivante est également valide:

char *ptr = "abcdefghi"; // the array "abcdefghi" decays to a pointer to the first element 'a'

Maintenant, dans votre cas, char str[][5] = {"what","is","this"}; est un tableau de 3 tableaux, chacun contenant 5 éléments.

Puisque les tableaux ne sont pas assignables, str[i] = "newstring"; n'est pas valide car str[i] est un tableau, mais str[i][j] = 'j'; est valide puisque str[i][j] est un élément de tableau qui n'est PAS un tableau lui-même et qui est assignable.


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.


  • 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