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?
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;
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);
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)
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
Modifier la ligne:
Char s[] = "tsa";
À:
Char *s = "tsa";
Et les choses deviendront plus claires
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.
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.
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.