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: %pn", s);
    printf("The direction of s is: %pn", &s);

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

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

    return 0;
}

Lors de la compilation 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 les types de pointeur incompatibles? Le nom d'un tableau n'est-il pas un pointeur vers son premier élément? Ainsi, si s est un pointeur sur 'a', &s doit être un char **, non? Et pourquoi ai-je les autres avertissements? Dois-je lancer les pointeurs avec (void *) pour les imprimer?

Et en cours d'exécution, j'obtiens 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 identiques?

33
demandé sur Sourav Ghosh 2008-10-13 18:25:38

8 réponses

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

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

char s[] = "asd";
char *p = &s[0];  // alternately you could use the shorthand char*p = s;
char **pp = &p;
24
répondu James Curran 2015-08-07 13:56:32

Oui, votre compilateur attend void *. Les jeter à vide *.

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
14
répondu indiv 2008-10-13 15:15:56

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

Pour tous les autres, bien que tous les pointeurs soient essentiellement des emplacements mémoire, ils sont toujours tapés, et le compilateur avertira d'assigner un type de pointeur vers 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; dit que s est une adresse mémoire, et le premier octet contient un caractère
  • char** ps; dit que ps est une adresse mémoire, et les quatre octets qui s'y trouvent (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
répondu Airsource Ltd 2008-10-13 14:43:06

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

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

De plus, les pages de manuel linux disent :

P

L'argument void * pointer est imprimé en hexadécimal (comme si par % # x ou % # lx). Il devrait être pointeur vers vide.

Vous pouvez changer votre code en:

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
répondu 4pie0 2016-10-03 13:25:51

Modifier la ligne:

Char s[] = "tsa";

À:

Char *s = "tsa";

Et les choses deviendront plus claires

1
répondu Sergey Golovchenko 2008-10-13 14:34:39

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. D'où s == &s. C'est juste une bizarrerie de la langue.

1
répondu Chris Conway 2008-10-13 14:38:15

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

1
répondu fizzer 2008-10-13 17:20:20

Vous avez utilisé:

char s[] = "asd";

Ici S pointe réellement vers les octets "asd". L'adresse de s, serait également pointer vers cet endroit.

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 de caractères, et a été réglé à l'adresse de caractères. En d'autres termes, vous avez trop d'indirection dans p. SI vous avez utilisé char *s = "tsa", vous pouvez utiliser cette indirection supplémentaire.

0
répondu selwyn 2008-10-13 14:49:43