Où les littéraux composé / chaîne sont-ils stockés dans la mémoire?

Je lis ça ;

Un littéral composé est une fonctionnalité C99 qui peut être utilisée pour créer un tableau sans nom. Considérons l’exemple:

int *p = (int []){3, 0, 3, 4, 1}; 

p pointe sur le premier élément d’un tableau de cinq éléments contenant 3, 0, 3, 4 et 1 .

En fait, je veux savoir si ce tableau est stocké en mémoire ou non car il n’a pas de nom.
En d’autres termes en cas de

 char* str = "hello" 

Où la chaîne "hello" sera stockée en mémoire?

C 2011 (N1570) 6.5.2.5 5 dit:

Si le littéral composé survient en dehors du corps d’une fonction, l’object a une durée de stockage statique. sinon, la durée de stockage automatique est associée au bloc englobant.

Ainsi, le littéral composé que vous affichez a une durée de stockage automatique. La norme C ne spécifie pas où de tels objects sont en mémoire. C’est à l’implémentation C d’organiser la mémoire. Généralement, un object avec une durée de stockage automatique est créé sur la stack, mais il existe d’autres méthodes permettant à une implémentation de gérer de tels objects.

En particulier, supposons que vous enregissortingez la valeur de p ailleurs et appeliez de manière récursive la routine contenant ce littéral composé. Lorsque la routine initialise à nouveau p , il existe une deuxième instance du littéral composé. Ce sont en fait des objects différents, et la norme C exige que leurs adresses soient différentes. Donc, si vous imprimez les deux pointeurs, ils auront des valeurs différentes. Toutefois, si l’optimiseur est en mesure de déterminer que vous ne le faites pas, que deux pointeurs vers des instances différentes du littéral composé ne sont jamais comparés et qu’aucun autre comportement observable (défini par le standard C) ne permet de les distinguer. , l’implémentation C est libre d’utiliser une instance réelle du littéral composé au lieu d’en créer une nouvelle à chaque fois. Dans ce cas, le compilateur pourrait conserver le littéral composé dans une section de données plutôt que dans la stack.

Voici un code qui montre que deux instances du même littéral composé ont des adresses différentes:

 #include  #include  void foo(int *q) { int *p = (int []) { 2, 3 }; if (!q) foo(p); else printf("p = %p, q = %p.\n", (void *) p, (void *) q); } int main(void) { foo(0); return 0; } 

Les littéraux de chaîne sont différents. C 2011 (N1570) 6.4.5 6 dit:

Dans la phase de traduction 7, un octet ou un code de valeur zéro est ajouté à chaque séquence de caractères multi-octets résultant d’un littéral de chaîne ou de littéraux. 78) La séquence de caractères multi-octets est ensuite utilisée pour initialiser un tableau de durée de stockage statique et de longueur juste suffisante pour contenir la séquence.

Ainsi, un littéral de chaîne désigne un object avec une durée de stockage statique. Il en existe une seule instance, même si une routine qui la contient est appelée de manière récursive.

Utilisation de l’arithmétique de pointeur. Alors

 p[0], p[1], ... 

ou

 *p, *(p + 1), ... 

Voici la chose. En C, vous avez de bons littéraux pour les types primitifs tels que int et char , et même des littéraux de chaîne. Donc, on peut facilement dire des choses comme

 int length(char *s); int len = length("Hello, World!"); 

En C99, le concept de littéraux composés a été ajouté pour gérer “littéral de tableau” et “littéral struct”. Par conséquent, nous pouvons maintenant dire des choses comme:

 int sum(int a[], int n); int total = sum((int []){ 17, 42 }, 2); 

Ceci utilise un littéral composé pour représenter un “littéral de tableau”.

En fait, je veux savoir si ce tableau est stocké en mémoire ou non car il n’a pas de nom.

Oui, en mémoire.

Je pense que votre confusion provient de cela. p a un nom. (int []){3, 0, 3, 4, 1} ne le fait pas. Il se trouve que la valeur de p est l’adresse de (int []){3, 0, 3, 4, 1} . Bien sûr (int []){3, 0, 3, 4, 1} est en mémoire; ce sera dans le segment de données de votre exécutable. Vous n’avez simplement aucun nom avec lequel vous pouvez vous référer.

Conceptuellement, comme si vous affectiez une chaîne à un pointeur de caractère en C, vous affectez un tableau d’entiers à p de type int* :
Lorsque vous déclarez: int *p = (int []){3, 0, 3, 4, 1}; on peut supposer être stocké en mémoire comme:

  p 23 27 31 35 36 37 +----+ +----+----+----+----+----+----+ | 23 | | 3 | 0 | 3 | 4 | 1 | ? | +----+ +----+----+----+----+----+----+ ▲ ▲ ▲ ▲ ▲ ▲ | | | | | // garbage value p p+1 p+2 p+3 p+4 

Donc, fondamentalement, tableau alloue continue mémoire. Et vous pouvez accéder aux éléments du tableau comme suit:

 p[0] == 3 p[1] == 0 p[2] == 3 p[3] == 4 p[4] == 1 

Remarque:

Nous faisons char* str = "hello"; Tandis que le type de littéraux de chaîne dans C est char[N] non pas char* . Mais dans la plupart des expressions, char[N] peut se décomposer en char* .

Point :

Lorsque vous déclarez un tableau, par exemple:

 int p[] = {3, 0, 3, 4, 1}; 

alors ici type est int[5] et &p pointer to an array = types est: int(*)[5]

Considérant que dans la déclaration:

 int *p = (int []){3, 0, 3, 4, 1}; 

p pointeur sur le premier élément et le type de p est int* et le type &p est int** . (Ceci est similaire à l’affectation de chaîne au pointeur de caractère).

Dans le premier cas, p[i] = 10; est légal pour 0 <= i <= 4 , mais dans le second cas, vous écrivez en lecture seule, erreur de segmentation de l'opération de mémoire illégale en mémoire.

point:

Ne pas suivre est aussi une déclaration valide:

 int *p = (int *){3, 0, 3, 4, 1}; 

Q En fait, je veux savoir si ce tableau est stocké en mémoire ou non car il n’a pas de nom.

Bien sûr, le tableau est stocké dans la mémoire, mais il vient d'être pointé par p ( p n'est pas le nom du tableau), aucun autre nom ne le désigne. Supposons que si vous le faites:

 int *p = (int []){3, 0, 3, 4, 1}; int i = 10; p = &i; 

Maintenant, vous avez perdu l'adresse du tableau, elle ressemble exactement à:

 char* s = "hello"; char c = 'A'; s = &c; 

et maintenant vous perdez l'adresse de "hello" .

La mémoire de constante provient d'un segment statique, lorsque vous le déclarez. Tout int-tableau de littéral de chaîne de caractères est stocké ici. Lorsque votre déclaration exécute une adresse atsortingbuée à des variables de pointeur. Mais les constantes n'ont pas de nom, mais de valeur. Dans les deux cas, le tableau et la chaîne "hello" deviennent la partie de l'exécutable dans la section de données. (vous pouvez démonter pour y trouver des valeurs).

Deux manières:

 &((int[]){3,0,3,4,1})[3] 

et

 ((int[]){3,0,3,4,1})+3 

Sachez que si le littéral est à l’intérieur d’une fonction, les pointeurs sur celle-ci sont invalidés à la sortie du bloc englobant.