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);
}
169

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]);
189
répondu Jon 2016-04-08 18:13:16

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:

  1. 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).
  2. 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

64
répondu JJJ 2012-04-18 07:00:31

C99 N1256 projet

il y a deux utilisations complètement différentes de tableaux littéraux:

  1. 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é.

  2. partout ailleurs::

    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 et t 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 utiliser p 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
23

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.

7
répondu potrzebie 2017-05-12 23:15:51

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";
5
répondu Jonathan Wood 2015-03-31 15:34:35

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

2
répondu CosminO 2012-04-17 07:16:14

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.

0
répondu Roee Gavirel 2012-04-17 07:33:21