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?
"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;
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);
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émoirechar* s;
indique que s est une adresse mémoire et que le premier octet contient un caractèrechar** 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)
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
changer de ligne:
char s [] = "asd";
à:
char * s = "asd";
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. Par conséquent s == &s
. C'est juste une bizarrerie de la langue.
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.
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.