web-dev-qa-db-fra.com

Les adresses de deux pointeurs de caractères vers des littéraux de chaîne différents sont identiques

#include<stdio.h>
#include<string.h>

int main()
{
    char * p = "abc";
    char * p1 = "abc";
    printf("%d %d", p, p1);
}

Lorsque j'imprime les valeurs des deux pointeurs, cela imprime la même adresse. Pourquoi?

80
seereddi sekhar

Que deux littéraux de chaîne différents avec le même contenu soient placés dans le même emplacement mémoire ou dans des emplacements mémoire différents dépend de l'implémentation.

Vous devez toujours traiter p et p1 comme deux pointeurs différents (même s'ils ont le même contenu) car ils peuvent ou non pointer vers la même adresse. Vous ne devez pas vous fier aux optimisations du compilateur.

C11 Standard, 6.4.5, littéraux de chaîne, sémantique

Il n'est pas précisé 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 n'est pas défini.


Le format d'impression doit être %p:

  printf("%p %p", (void*)p, (void*)p1);

Voir cette réponse pour savoir pourquoi.

86
P.P.

Votre compilateur semble être assez intelligent, détectant que les deux littéraux sont identiques. Et comme les littéraux sont constants, le compilateur a décidé de ne pas les stocker deux fois.

Il semble utile de mentionner que cela ne doit pas nécessairement être le cas. Veuillez voir Blue Moon 's réponse à cela .


Btw: l'instruction printf() devrait ressembler à ceci

printf("%p %p", (void *) p, (void *) p1);

comme "%p" doit être utilisé pour imprimer les valeurs du pointeur, et il est défini pour le pointeur de type void * seulement.*1


Je dirais également que le code manque une instruction return, mais la norme C semble être en train d'être modifiée. D'autres pourraient bien vouloir clarifier cela.


* 1: Casting vers void * ici n'est pas nécessaire pour char * pointeurs, mais pour les pointeurs vers tous les autres types.

28
alk

Votre compilateur a fait quelque chose appelé "regroupement de chaînes". Vous avez spécifié que vous vouliez deux pointeurs, tous deux pointant vers le même littéral de chaîne - il n'a donc fait qu'une seule copie du littéral.

Techniquement: il aurait dû se plaindre de ne pas avoir rendu les pointeurs "const"

const char* p = "abc";

Cela est probablement dû au fait que vous utilisez Visual Studio ou que vous utilisez GCC sans -Wall.

Si vous souhaitez expressément qu'ils soient stockés deux fois en mémoire, essayez:

char s1[] = "abc";
char s2[] = "abc";

Ici, vous déclarez explicitement que vous voulez deux tableaux de caractères de chaîne c plutôt que deux pointeurs vers des caractères.

Mise en garde: le regroupement de chaînes est une fonction de compilation/optimisation et non une facette du langage. En tant que tels compilateurs différents dans des environnements différents produiront un comportement différent en fonction de choses comme le niveau d'optimisation, les indicateurs de compilateur et si les chaînes sont dans différentes unités de compilation.

18
kfsone

Comme d'autres l'ont dit, le compilateur remarque qu'ils ont la même valeur et décide donc de les faire partager des données dans l'exécutable final. Mais ça devient plus chic: quand je compile ce qui suit avec gcc -O

#include<stdio.h>
#include<string.h>

int main()
{
  char * p = "abcdef";
  char * p1 = "def";
  printf("%d %d", p, p1);
}

il imprime 4195780 4195783 pour moi. C'est, p1 commence 3 octets après p, donc GCC a vu le suffixe commun de def (y compris le \0 terminator) et fait une optimisation similaire à celle que vous avez montrée.

(Ceci est une réponse car il est trop long pour être un commentaire.)

14
huon

Les littéraux de chaîne dans le code sont stockés dans un segment de données en lecture seule du code. Lorsque vous écrivez un littéral de chaîne comme "abc", il retourne en fait un "const char *" et si vous aviez tous les avertissements du compilateur, il vous dirait que vous effectuez un cast à ce stade. Vous n'êtes pas autorisé à modifier ces chaînes pour la raison même que vous avez indiquée dans cette question.

3
Salgar

En fait dépend du compilateur que vous utilisez.

Dans mon système avec TC++ 3.5 il imprime deux valeurs différentes pour les deux pointeurs, c'est-à-dire deux adresses différentes.

Votre compilateur est conçu pour qu'il vérifie l'existence de toute valeur dans la mémoire et en fonction de son existence il réaffectera ou tilisez la même référence de la valeur précédemment stockée si la même valeur est mentionnée.

Alors n'y pensez pas trop car cela dépend de la façon dont le compilateur analyse le code.

C'EST TOUT ...

2
Rajesh Paul

Lorsque vous créez un littéral de chaîne ("abc"), il est enregistré dans une mémoire qui contient des littéraux de chaîne, puis il est réutilisé si vous vous référez au même littéral de chaîne, les deux pointeurs pointant ainsi vers le même emplacement, où le " Le littéral de chaîne abc "est stocké.

J'ai appris cela il y a quelque temps, donc je ne l'ai peut-être pas expliqué clairement, désolé.

2
Lord Zsolt

C'est l'optimisation du compilateur mais oubliez l'optimisation pour la portabilité. Parfois, les codes compilés sont plus lisibles que les codes réels.

1
Amir Saniyan

car la chaîne "abc" lui-même une adresse en mémoire. quand tu réécris "abc" il stocke la même adresse

1
SANDEEP

vous utilisez littéral de chaîne,

quand complier attrape deux mêmes chaînes littérales,

il donne le même emplacement de mémoire, donc il montre le même emplacement de pointeur./

0
Dev