J'essaie de comprendre les pointeurs en C mais je suis actuellement confus avec ce qui suit:
char *p = "hello"
Il s'agit d'un pointeur de caractère pointant sur le tableau de caractères, commençant à h .
char p[] = "hello"
Ceci est un tableau qui stocke bonjour .
Quelle est la différence lorsque je passe ces deux variables dans cette fonction?
void printSomething(char *p)
{
printf("p: %s",p);
}
char p[3] = "hello";
la chaîne d'initialisation est trop longue pour la taille du tableau que vous déclarez. Faute de frappe?char p[]="hello";
suffirait tout simplement !char
spécifique.Réponses:
char*
etchar[]
sont de types différents , mais ce n'est pas immédiatement apparent dans tous les cas. Cela est dû au fait que les tableaux se désintègrent en pointeurs , ce qui signifie que si une expression de typechar[]
est fournie là où l'un de typechar*
est attendu, le compilateur convertit automatiquement le tableau en un pointeur vers son premier élément.Votre exemple de fonction
printSomething
attend un pointeur, donc si vous essayez de lui passer un tableau comme ceci:Le compilateur prétend que vous avez écrit ceci:
la source
printf
gère la%s
chaîne de format: commencez à l'adresse fournie et continuez jusqu'à rencontrer un terminateur nul. Si vous souhaitez imprimer un seul caractère, vous pouvez utiliser la%c
chaîne de formatage, par exemple.char *p = "abc";
le caractère NULL\0
est automatiquement ajouté comme dans le cas du tableau char []?char *name; name="123";
mais peut faire la même chose avec leint
type? Et après avoir utilisé%c
pour imprimername
, la sortie est une chaîne illisible:�
?Voyons voir:
foo * et foo [] sont de types différents et ils sont traités différemment par le compilateur (pointeur = adresse + représentation du type du pointeur, tableau = pointeur + longueur facultative du tableau, si connu, par exemple, si le tableau est alloué statiquement ), les détails peuvent être trouvés dans la norme. Et au niveau de l'exécution aucune différence entre eux (en assembleur, enfin presque, voir ci-dessous).
En outre, il existe une question connexe dans la FAQ C :
la source
C99 N1256 draft
Il existe deux utilisations différentes des littéraux de chaîne de caractères:
Initialiser
char[]
:C'est "plus magique", et décrit au 6.7.8 / 14 "Initialisation":
Ce n'est donc qu'un raccourci pour:
Comme tout autre tableau régulier,
c
peut être modifié.Partout ailleurs: il génère un:
Donc, quand vous écrivez:
Ceci est similaire à:
Notez la conversion implicite de
char[]
àchar *
, qui est toujours légale.Ensuite, si vous modifiez
c[0]
, vous modifiez également__unnamed
, qui est UB.Ceci est documenté en 6.4.5 "Littéraux de chaîne":
6.7.8 / 32 "Initialisation" donne un exemple direct:
Mise en œuvre de GCC 4.8 x86-64 ELF
Programme:
Compiler et décompiler:
La sortie contient:
Conclusion: GCC le stocke
char*
dans la.rodata
section, pas dans.text
.Si nous faisons de même pour
char[]
:on obtient:
donc il est stocké dans la pile (par rapport à
%rbp
).Notez cependant que le script de l'éditeur de liens par défaut place
.rodata
et.text
dans le même segment, qui a une autorisation d'exécution mais pas d'écriture. Cela peut être observé avec:qui contient:
la source
Vous n'êtes pas autorisé à modifier le contenu d'une constante de chaîne, ce à quoi
p
pointe le premier . Le secondp
est un tableau initialisé avec une constante chaîne, et vous pouvez changer son contenu.la source
Pour des cas comme celui-ci, l'effet est le même: vous finissez par passer l'adresse du premier caractère dans une chaîne de caractères.
Mais les déclarations ne sont évidemment pas les mêmes.
Ce qui suit met de côté la mémoire pour une chaîne et également un pointeur de caractère, puis initialise le pointeur pour pointer vers le premier caractère de la chaîne.
Alors que ce qui suit met de côté la mémoire juste pour la chaîne. Il peut donc utiliser moins de mémoire.
la source
Pour autant que je me souvienne, un tableau est en fait un groupe de pointeurs. Par exemple
est une vraie déclaration
la source
*(arr + 1)
vous amène au deuxième membre dearr
. Si*(arr)
pointe vers une adresse de mémoire 32 bits, par exemplebfbcdf5e
, alors*(arr + 1)
pointe versbfbcdf60
(le deuxième octet). Par conséquent, pourquoi sortir de la portée d'un tableau entraînera des résultats étranges si le système d'exploitation ne se bloque pas. Siint a = 24;
est à l'adressebfbcdf62
, l'accèsarr[2]
peut revenir24
, en supposant qu'un défaut de segmentation ne se produit pas en premier.De l' APUE , section 5.14:
Le texte cité correspond à l'explication de @Ciro Santilli.
la source
char p[3] = "hello"
? devrait sechar p[6] = "hello"
rappeler qu'il y a un caractère '\ 0' à la fin d'une "chaîne" en C.quoi qu'il en soit, le tableau en C n'est qu'un pointeur vers le premier objet d'un objet à ajuster dans la mémoire. les seuls différents sont en sémantique. tandis que vous pouvez modifier la valeur d'un pointeur pour pointer vers un emplacement différent dans la mémoire, un tableau, une fois créé, pointera toujours vers le même emplacement.
également lorsque vous utilisez un tableau, les «nouveaux» et «supprimer» sont automatiquement effectués pour vous.
la source