web-dev-qa-db-fra.com

Impression de pointeurs en C

J'essayais de comprendre quelque chose avec des pointeurs, alors j'ai écrit ce code:

#include <stdio.h>

int main(void)
{
    char s[] = "asd";
    char **p = &s;

    printf("The value of s is: %p\n", s);
    printf("The direction of s is: %p\n", &s);

    printf("The value of p is: %p\n", p);
    printf("The direction of p is: %p\n", &p);

    printf("The direction of s[0] is: %p\n", &s[0]);
    printf("The direction of s[1] is: %p\n", &s[1]);
    printf("The direction of s[2] is: %p\n", &s[2]);

    return 0;
}

Lorsque je le compile avec gcc, j'obtiens ces avertissements:

$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm
main.c: In function ‘main’:
main.c:6: warning: initialization from incompatible pointer type
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’

(Les drapeaux pour gcc sont parce que je dois être C89)

Pourquoi des types de pointeurs incompatibles? Le nom d'un tableau n'est-il pas un pointeur vers son premier élément? Donc, si s est un pointeur vers 'a', &s Doit être un char **, non? Et pourquoi reçois-je les autres avertissements? Dois-je lancer les pointeurs avec (void *) pour les imprimer?

Et lorsque je cours, je reçois quelque chose comme ceci:

$ ./main-bin
The value of s is: 0xbfb7c860
The direction of s is: 0xbfb7c860
The value of p is: 0xbfb7c860
The direction of p is: 0xbfb7c85c
The direction of s[0] is: 0xbfb7c860
The direction of s[1] is: 0xbfb7c861
The direction of s[2] is: 0xbfb7c862

Comment la valeur de s et sa direction (et bien sûr la valeur de p) peuvent-elles être les mêmes?

38
alcuadrado

"s" n'est pas un "char *", c'est un "char [4]". Et donc, "& s" n'est pas un "char **", mais en fait "un pointeur vers un tableau de 4 caractères". Votre compilateur peut traiter "& s" comme si vous aviez écrit "& s [0]", ce qui est à peu près la même chose, mais c'est un "char *".

Lorsque vous écrivez "char ** p = & s;" vous essayez de dire "Je veux que p soit mis à l'adresse de la chose qui pointe actuellement vers" asd ". Mais actuellement il n'y a rien qui points à" asd ". Il y a juste un tableau qui détient "asd";

char s[] = "asd";
char *p = &s[0];  // alternately you could use the shorthand char*p = s;
char **pp = &p;
25
James Curran

Oui, votre compilateur attend void *. Jetez-les simplement pour annuler *.

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
15
indiv

Si vous passez le nom d'un tableau comme argument à une fonction, il est traité comme si vous aviez passé l'adresse du tableau. So & s et s sont des arguments identiques. Voir K&R 5.3. & s [0] est identique à & s, car il prend l'adresse du premier élément du tableau, ce qui revient à prendre l'adresse du tableau lui-même.

Pour tous les autres, bien que tous les pointeurs soient essentiellement des emplacements de mémoire, ils sont toujours tapés, et le compilateur vous avertira d'assigner un type de pointeur à un autre.

  • void* p; dit que p est une adresse mémoire, mais je ne sais pas ce qu'il y a dans la mémoire
  • char* s; indique que s est une adresse mémoire et que le premier octet contient un caractère
  • char** ps; indique que ps est une adresse mémoire et que les quatre octets (pour un système 32 bits) contiennent un pointeur de type char *.

cf http://www.oberon2005.ru/paper/kr_c.pdf (version e-book de K&R)

4
Airsource Ltd

Ce n'est pas un pointeur vers le caractère char* mais un pointeur sur un tableau de 4 caractères: char* [4]. Avec g ++, il ne compile pas:

main.cpp: dans la fonction "int main (int, char **)": main.cpp: 126: erreur: impossible de convertir "char (*) [4]" en "char **" lors de l'initialisation

De plus, les pages de manuel Linux dit :

p

L'argument du pointeur void * est imprimé en hexadécimal (comme par% # x ou% # lx). Ce devrait être le pointeur du vide.

Vous pouvez changer votre code pour:

char* s = "asd";
char** p = &s;

printf("The value of s is: %p\n", s);
printf("The address of s is: %p\n", &s);

printf("The value of p is: %p\n", p);
printf("The address of p is: %p\n", &p);

printf("The address of s[0] is: %p\n", &s[0]);
printf("The address of s[1] is: %p\n", &s[1]);
printf("The address of s[2] is: %p\n", &s[2]);

résultat:

La valeur de s est: 0x403f00

L'adresse de s est: 0x7fff2df9d588

La valeur de p est: 0x7fff2df9d588

L'adresse de p est: 0x7fff2df9d580

L'adresse de s [0] est: 0x403f00

L'adresse de s [1] est: 0x403f01

L'adresse de s [2] est: 0x403f02

3
4pie0

changer de ligne:

char s [] = "asd";

à:

char * s = "asd";

et les choses deviendront plus claires

1
Sergey Golovchenko

Vous ne pouvez pas modifier la valeur (c'est-à-dire l'adresse) d'un tableau statique. En termes techniques, la lvalue d'un tableau est l'adresse de son premier élément. Par conséquent s == &s. C'est juste une bizarrerie de la langue.

1
Chris Conway

Normalement, il est considéré comme un style médiocre de lancer inutilement des pointeurs vers (void *). Ici, cependant, vous avez besoin des transtypages (void *) sur les arguments printf car printf est variadic. Le prototype ne dit pas au compilateur dans quel type convertir les pointeurs sur le site d'appel.

1
fizzer

Vous avez utilisé:

char s[] = "asd";

Ici, il pointe vers les octets "asd". L'adresse de s, indiquerait également cet emplacement.

Si vous avez utilisé:

char *s = "asd";

la valeur de s et & s serait différente, car s serait en fait un pointeur vers les octets "asd".

Vous avez utilisé:

char s[] = "asd";
char **p = &s;

Ici, s pointe vers les octets "asd". p est un pointeur vers un pointeur sur des caractères et a été défini sur l'adresse des caractères. En d'autres termes, vous avez trop d'indirections en p. Si vous avez utilisé char * s = "asd", vous pouvez utiliser cette indirection supplémentaire.

0
selwyn