Que signifie le “nom de tableau” dans le cas d’un tableau de pointeurs de caractères?

Dans mon code:

char *str[] = {"forgs", "do", "not", "die"}; printf("%d %d", sizeof(str), sizeof(str[0])); 

Je reçois la sortie en tant que 12 2 , donc mes doutes sont les suivants:

  1. Pourquoi y a-t-il une différence?
  2. str et str[0] sont des indicateurs de caractère, non?

Dans la plupart des cas, un nom de tableau décroîtra avec la valeur de l’adresse de son premier élément et le type étant identique à un pointeur sur le type d’élément. Donc, vous vous attendriez à ce qu’une str nue ait la valeur égale à &str[0] avec le type pointeur à pointeur sur caractère.

Cependant, ce n’est pas le cas pour sizeof . Dans ce cas, le nom du tableau conserve son type pour sizeof , qui serait un tableau de 4 pointeurs sur char .

Le type de retour de sizeof est un size_t . Si vous avez un compilateur C99, vous pouvez utiliser %zu dans la chaîne de format pour imprimer la valeur renvoyée par sizeof .

La réponse à la question est déjà acceptée et acceptée, mais j’ajoute une description plus détaillée (répondant également à la question initiale) qui, je suppose, sera utile pour les nouveaux utilisateurs. (Comme j’ai cherché, cette description n’est expliquée nulle part ailleurs (du moins sur stackoverflow) donc j’ajoute maintenant.

Première lecture: sizeof opérateur

6.5.3.4 L’opérateur sizeof, 1125:
Lorsque vous appliquez l’opérateur sizeof à un type de tableau, le résultat est le nombre total d’octets dans le tableau.

Selon cela, lorsque sizeof est appliqué au nom d’un identifiant de tableau statique ( non alloué via malloc), le résultat est la taille en octets de l’ensemble du tableau plutôt que simplement l’adresse. C’est l’une des rares exceptions à la règle selon laquelle le nom d’un tableau est converti / décomposé en un pointeur sur le premier élément du tableau , ce qui est possible uniquement parce que la taille réelle du tableau est fixe et connue au moment de la compilation. sizeof opérateur évalue.

Pour mieux comprendre, considérons le code ci-dessous:

 #include int main(){ char a1[6], // One dimensional a2[7][6], // Two dimensional a3[5][7][6]; // Three dimensional printf(" sizeof(a1) : %lu \n", sizeof(a1)); printf(" sizeof(a2) : %lu \n", sizeof(a2)); printf(" sizeof(a3) : %lu \n", sizeof(a3)); printf(" Char : %lu \n", sizeof(char)); printf(" Char[6] : %lu \n", sizeof(char[6])); printf(" Char[5][7] : %lu \n", sizeof(char[7][6])); printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6])); return 1; } 

Sa sortie:

  sizeof(a1) : 6 sizeof(a2) : 42 sizeof(a3) : 210 Char : 1 Char[5] : 6 Char[5][7] : 42 Char[5][7][6]: 210 

Vérifiez ci-dessus que vous travaillez à @ codepad , remarquez que la taille du caractère est d’un octet, si vous remplacez caractère par int dans le programme ci-dessus, chaque sortie sera multipliée par sizeof(int) sur votre machine.

Différence entre char* str[] et char str[][] et comment les deux sont stockés en mémoire

Declaration-1: char *str[] = {"forgs", "do", "not", "die"};

Dans cette déclaration, str[] est un tableau de pointeurs sur char. Chaque index str[i] pointe sur le premier caractère des chaînes dans {"forgs", "do", "not", "die"}; .
Logiquement, str devrait être rangé en mémoire de la manière suivante:

 Array Variable: Constant Ssortingngs: --------------- ----------------- str: 201 202 203 204 205 206 +--------+ +-----+-----+-----+-----+-----+-----+ 343 | |= *(str + 0) | 'f' | 'o' | 'r' | 'g' | 's' | '\0'| | str[0] |-------| +-----+-----+-----+-----+-----+-----+ | 201 | +-----------▲ +--------+ 502 503 504 | | +-----+-----+-----+ 347 | str[1] |= *(str + 1) | 'd' | 'o' | '\0'| | 502 |-------| +-----+-----+-----+ +--------+ +-----------▲ | | 43 44 45 46 351 | 43 | +-----+-----+-----+-----+ | str[2] |= *(str + 2) | 'n' | 'o' | 't' | '\0'| | |-------| +-----+-----+-----+-----+ +--------+ +-----------▲ 355 | | | 9002 | 9002 9003 9004 9005 | str[3] | +-----+-----+-----+-----+ | |= *(str + 3) | 'd' | 'i' | 'e' | '\0'| +--------+ | +-----+-----+-----+-----+ +-----------▲ Diagram: shows that str[i] Points to first char of each constant ssortingng literal. Memory address values are assumption. 

Remarque: str[] est stocké dans les allocations de mémoire continue et chaque chaîne est stockée en mémoire à une adresse aléatoire (pas dans l’espace de poursuite).

[RÉPONSE]

Selon le code suivant Codepad :

 int main(int argc, char **argv){ char *str[] = {"forgs", "do", "not", "die"}; printf("sizeof(str): %lu, sizeof(str[0]): %lu\n", sizeof(str), sizeof(str[0]) ); return 0; } 

Sortie:

 sizeof(str): 16, sizeof(str[0]): 4 
  • Dans ce code, str est un tableau pour 4 adresses de caractères, où chaque caractère char* taille de 4 octets. Par conséquent, selon la citation ci-dessus, la taille totale du tableau est de 4 * sizeof(char*) = 16 octets.

  • Le type de données de str est char*[4] .

  • str[0] n’est rien d’autre qu’un pointeur sur char, donc ses quatre octets. Le type de données de str[i] est char* .

(note: dans certains systèmes, l’adresse peut être 2 octets ou 8 octets)

En ce qui concerne la sortie, il convient également de lire le commentaire de glglgl à la question:

Quelle que soit votre architecture, la première valeur doit être 4 fois la seconde. Sur un ordinateur 32 bits, vous devriez obtenir 16 4, sur un 64 bits, un 32 8. Sur un très ancien ordinateur ou sur un système embarqué, vous pourriez même obtenir 8 2, mais jamais 12 2, car le tableau contient 4 éléments du même taille

Points supplémentaires:

  • Étant donné que chaque str[i] désigne un caractère char* (et que la chaîne) est variable, on peut atsortingbuer à str[i] l’adresse d’une nouvelle chaîne, par exemple: str[i] = "yournewname"; est valable pour i = 0 to < 4 .

Un autre point important à noter:

  • Dans notre exemple, str[i] pointe vers le littéral chaîne constant qui ne peut pas être modifié; par conséquent, str[i][j] = 'A' n'est pas valide (nous ne pouvons pas écrire en mémoire morte) et le faire sera une erreur d'exécution.
    Mais supposons que si str[i] pointe sur un tableau de caractères simple, alors str[i][j] = 'A' peut être une expression valide.
    Considérons le code suivant:

      char a[] = "Hello"; // a[] is simple array char *str[] = {"forgs", "do", "not", "die"}; //str[0][4] = 'A'; // is error because writing on read only memory str[0] = a; str[0][5] = 'A'; // is perfectly valid because str[0] // points to an array (that is not constant) 

Vérifiez ici le code de travail: Codepad

Declaration-2: char str[][6] = {"forgs", "do", "not", "die"}; :

Ici str est un tableau bidimensionnel de caractères (où chaque ligne est de taille égale) de taille 4 * 6. (rappelez-vous ici que vous devez donner la valeur de colonne dans la déclaration de str explicitement, mais row est 4 en raison du nombre de chaînes 4)
En mémoire, str[][] sera comme ci-dessous dans le diagramme:

  str +---201---202---203---204---205---206--+ 201 | +-----+-----+-----+-----+-----+-----+| str[0] = *(str + 0)--►| 'f' | 'o' | 'r' | 'g' | 's' | '\0'|| 207 | +-----+-----+-----+-----+-----+-----+| str[1] = *(str + 1)--►| 'd' | 'o' | '\0'| '\0'| '\0'| '\0'|| 213 | +-----+-----+-----+-----+-----+-----+| str[2] = *(str + 2)--►| 'n' | 'o' | 't' | '\0'| '\0'| '\0'|| 219 | +-----+-----+-----+-----+-----+-----+| str[3] = *(str + 3)--►| 'd' | 'i' | 'e' | '\0'| '\0'| '\0'|| | +-----+-----+-----+-----+-----+-----+| +--------------------------------------+ In Diagram: str[i] = *(str + i) = points to a complete i-row of size = 6 chars. str[i] is an array of 6 chars. 

Cet arrangement de tableau 2D en mémoire est appelé Row-Major : un tableau multidimensionnel en mémoire linéaire est organisé de manière à ce que les rangées soient stockées les unes après les autres. C'est l'approche utilisée par le langage de programmation C.

Notez les différences dans les deux diagrammes.

  • Dans le second cas, un tableau de caractères bidimensionnel complet est alloué dans la mémoire continue.
  • Pour tout i = 0 to 2 , str[i] et str[i + 1] sont différents de 6 octets (ce qui équivaut à la longueur d'une ligne).
  • La ligne double limite dans ce diagramme signifie que str représente 6 * 4 complet = 24 caractères.

Considérons maintenant le code similaire que vous avez posté dans votre question pour le tableau de caractères à 2 dimensions, consultez Codepad :

 int main(int argc, char **argv){ char str[][6] = {"forgs", "do", "not", "die"}; printf("sizeof(str): %lu, sizeof(str[0]): %lu\n", sizeof(str), sizeof(str[0]) ); return 0; } 

Sortie:

 sizeof(str): 24, sizeof(str[0]): 6 

En fonction du traitement de la taille par array de l'opérateur, Lors de l'application d'une taille de tableau à 2 jours de devrait renvoyer la taille totale du tableau qui est de 24 octets.

  • Comme nous le soaps, l’opérateur sizeof renvoie la taille de l’ensemble du tableau lors de l’application du nom du tableau. Donc, pour sizeof(str) il retourne = 24, c'est-à-dire que la taille d'un tableau de caractères 2D complet se compose de 24 caractères (6 colonnes * 4 lignes).

  • Dans cette déclaration, le type de str est char[4][6] .

  • Un autre point intéressant est que str[i] représente un tableau de discussions et que son type est char[6] . Et sizeof(str[0]) est la taille complète du tableau = 6 (longueur de la ligne).

Points supplémentaires:

  • Dans la deuxième déclaration, str[i][j] n'est pas constant et son contenu peut être modifié. Par exemple, str[i][j] = 'A' est une opération valide.

  • str[i] est le nom du tableau de caractères de type char[6] est une constante et l'affectation à str[i] par exemple, str[i] = "newssortingng" est une opération illégale (l'infecter sera une erreur de compilation).

Une autre différence importante entre deux déclarations:

Dans Declaration-1 : char *str[] = {"forgs", "do", "not", "die"}; , type de &str est char*(*)[4] , son adresse de tableau de pointeurs de caractères.

Dans Declaration-2 : char str[][6] = {"forgs", "do", "not", "die"}; , type de &str est char(*)[4][6] , son adresse de tableau de caractères 2D de 4 lignes et 6 colonnes.

Si on veut lire une description similaire pour un tableau 1-D: Que renvoie sizeof(&array) ?

C’est 16 4 sur mon ordinateur, et je peux expliquer ceci: str est un tableau de caractères char* , donc sizeof(str)==sizeof(char*)*4

Je ne sais pas pourquoi vous obtenez 12 2 cependant.

Les deux pointeurs sont différents. str est un array of char pointers , dans votre exemple, un ( char*[4] ) et str[0] est un char pointer .

La première taille renvoie la taille des quatre pointeurs de caractères qui contient, et la seconde renvoie la taille du caractère char* .
Dans mes tests, les résultats sont:

 sizeof(str[0]) = 4 // = sizeof(char*) sizeof(str) = 16 = sizeof(str[0]) + sizeof(str[1]) + sizeof(str[2]) + sizeof(str[3]) = 4 * sizeof(char*) = 4 * 4 = 16