Quelle est la différence entre le tableau de char vs pointeur de char en C?
j'essaie de comprendre les pointeurs en C mais je suis actuellement confondu avec ce qui suit:
-
char *p = "hello"
il s'agit d'un pointeur de char pointant vers le tableau de caractères, commençant par h .
-
char p[] = "hello"
C'est un tableau qui stocke bonjour .
Quelle est la différence quand je passe ces deux variables dans cette fonction?
void printSomething(char *p)
{
printf("p: %s",p);
}
7 réponses
char*
et char[]
sont des 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écomposent en pointeurs , ce qui signifie que si une expression de type char[]
est fournie où une expression de type char*
est attendue, 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 passer un tableau comme cela:
char s[10] = "hello";
printSomething(s);
le compilateur prétend que vous avez écrit ceci:
char s[10] = "hello";
printSomething(&s[0]);
Voyons voir:
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello"; // no need to count this
printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both
// size_t strlen(const char *s) and we don't get any warnings here:
printf("%zu\n", strlen(p)); // => 5
printf("%zu\n", strlen(q)); // => 5
return 0;
}
foo* et foo [] sont des types différents et ils sont traités différemment par le compilateur (pointeur = adresse + représentation du type du pointeur, tableau = pointeur + longueur optionnelle du tableau, si connue, par exemple, si le tableau est attribué statiquement), les détails peuvent être trouvés dans le standard. Et au niveau de l'exécution aucune différence entre eux (en assembleur, Eh bien, presque, voir ci-dessous).
aussi, il est une question dans le C FAQ :
Q : Quelle est la différence entre ces initialisations?
char a[] = "string literal"; char *p = "string literal";
mon programme s'écrase si j'essaie d'attribuer une nouvelle valeur à p[i].
A : une chaîne littérale (le terme formel pour une chaîne double citation dans C source) peut être utilisée dans deux légèrement différents façons:
- Comme l'initialiseur, pour un tableau de char, comme dans la déclaration de char a[] , il spécifie les valeurs initiales des personnages dans ce tableau (et, si nécessaire, sa taille).
- N'importe où ailleurs, il se transforme en un tableau statique sans nom de caractères, et ce tableau sans nom peut être stocké dans la mémoire en lecture seule, et qui ne peut donc pas nécessairement être modifié. Dans un contexte d'expression, le tableau est converti immédiatement en pointeur, comme d'habitude (voir section 6), de sorte que la deuxième déclaration initialise p pour pointer vers le premier élément du tableau sans nom.
certains compilateurs ont un commutateur contrôlant si les caractères littéraux de chaîne sont inscriptibles ou non (pour compiler l'ancien code), et certains peuvent avoir des options pour faire traiter formellement les caractères littéraux de chaîne comme des tableaux de caractères de const (pour une meilleure capture d'erreur).
voir également les questions 1.31, 6.1, 6.2, 6.8 et 11.8 B.
Références: K&R2 Secondes de 5,5 p. 104
ISO sec. 6.1.4, sec. 6.5.7
Rationale Sec. 3.1.4
H&S sec. 2.7.4 pp. 31-2
C99 N1256 projet
il y a deux utilisations complètement différentes de tableaux littéraux:
-
Initialiser
char[]
:char c[] = "abc";
c'est "plus de magie", et décrit à 6.7.8 / 14 "initialisation " :
un tableau de caractères peut être initialisé par une chaîne de caractères littérale, éventuellement entre accolades. Les caractères successifs de la chaîne de caractères littéral (y compris le terminez le caractère null s'il y a de la place ou si le tableau est de taille inconnue) initialisez les éléments de la matrice.
donc ce n'est qu'un raccourci pour:
char c[] = {'a', 'b', 'c', '"151910920"'};
comme tout autre tableau régulier,
c
peut être modifié. -
partout ailleurs::
- sans nom
- tableau de char qu'est-Ce que le type de littéraux de chaîne en C et C++?
- avec stockage statique
- qui donne UB si modifié
donc quand vous écrivez:
char *c = "abc";
similaire à:
/* __unnamed is magic because modifying it gives UB. */ static char __unnamed[] = "abc"; char *c = __unnamed;
noter la fonte implicite de
char[]
àchar *
, ce qui est toujours légal.puis si vous modifiez
c[0]
, vous modifiez aussi__unnamed
, qui est UB.C'est documentée à l' 6.4.5 "littéraux de Chaîne" :
5 dans la phase de traduction 7, un octet ou code de valeur Zéro est ajouté à chaque multi-octets séquence de caractères résultant d'une chaîne littérale ou littérale. Les caractères codés sur plusieurs octets la séquence est alors utilisée pour initialiser un tableau de stockage statique de la durée et de la longueur juste suffisant pour contenir la séquence. Pour la chaîne de caractères littéraux, les éléments du tableau ont type char, et sont initialisés avec les octets individuels du caractère multibyte séquence.[ ..]
6 il n'est pas précisé si ces matrices sont distinctes à condition que leurs éléments aient la forme les valeurs appropriées. Si le programme tente de modifier un tel tableau, le comportement est indéterminé.
6.7.8/32 "L'initialisation " donne un exemple direct:
exemple 8: la déclaration
char s[] = "abc", t[3] = "abc";
définit les objets" char array
s
ett
dont les éléments sont initialisés par des caractères littéraux.cette déclaration est identique à
char s[] = { 'a', 'b', 'c', '"151950920"' }, t[] = { 'a', 'b', 'c' };
le contenu des tableaux est modifiable. D'autre part, la déclaration
char *p = "abc";
définit
p
avec le type "pointeur vers char" et l'initialise pour pointer vers un objet avec le type "tableau de char" avec la longueur 4 dont les éléments sont initialisés avec une chaîne de caractères littérale. Si une tentative est faite pour utiliserp
pour modifier le contenu du tableau, le comportement est indéfini.
GCC 4.8 x86-64 ELF implementation
programme:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
compiler et décompiler:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
contient:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq "151990920"x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Conclusion: GCC magasins char*
il en .rodata
, pas dans .text
.
si nous faisons la même chose pour char[]
:
char s[] = "abc";
nous obtenons:
17: c7 45 f0 61 62 63 00 movl "1519110920"x636261,-0x10(%rbp)
donc, il est stocké dans la pile (par rapport à %rbp
).
notez cependant que le script de linker par défaut met .rodata
et .text
dans le même segment, qui a exécuter mais pas de permission d'écriture. Cela peut être observé avec:
readelf -l a.out
qui contient:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
vous n'êtes pas autorisé à changer le contenu d'une constante de chaîne, ce qui est ce que le premier p
pointe. Le second p
est un tableau initialisé avec une constante de chaîne, et vous pouvez changer son contenu.
pour des cas comme celui-ci, l'effet est le même: on finit par passer l'adresse du premier caractère dans une chaîne de caractères.
Les déclarations ne sont évidemment pas la même.
réserve de la mémoire pour une chaîne de caractères et aussi un pointeur de caractère, et puis initialise le pointeur sur le premier caractère de la chaîne.
char *p = "hello";
alors que le suivant met de côté la mémoire juste pour le chaîne. Donc elle peut utiliser moins de mémoire.
char p[10] = "hello";
autant que je me souvienne, un tableau est en fait un groupe de pointeurs. Par exemple
p[1]== *(&p+1)
est une vraie déclaration
char p[3] = "hello"
? devrait être char p[6] = "hello"
rappelez-vous qu'il y a un' \0 ' char à 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 adjust dans la mémoire. les seules différences sont dans la sémantique. si l'on peut changer la valeur d'un pointeur vers un autre emplacement dans la mémoire d'un tableau, d'après créées, pointe toujours vers le même emplacement.
aussi lors de l'utilisation du tableau le "nouveau" et "supprimer" est fait automatiquement pour vous.