Lorsque vous traitez avec des pointeurs, vous devez garder les deux points suivants fermement à l'avant de votre esprit:
# 1 Le pointeur se est séparé des données qu'il points. Le pointeur est juste un nombre. Le nombre nous indique où, en mémoire, nous pouvons trouver le début de certains autres morceau de données. Un pointeur peut être utilisé pour accéder aux données points à, mais nous pouvons également manipuler la valeur du pointeur lui-même. Lorsque nous augmentons (ou diminuons) la valeur du pointeur lui-même, nous déplaçons la «destination» du pointeur vers l'avant (ou vers l'arrière) à partir de l'endroit vers lequel il pointait à l'origine. Cela nous amène au deuxième point ...
# 2 Chaque variable pointeur a un type qui indique quel type de données est en cours pointé. Un char *
indique un char
; un int *
points à un int
; etc. Un pointeur peut même pointer vers un autre pointeur (char **
). Le type est important, car lorsque le compilateur applique des opérations arithmétiques à une valeur de pointeur, il prend automatiquement en compte la taille du type de données pointé vers. Cela nous permet de traiter avec des tableaux en utilisant l'arithmétique des pointeurs simple:
int *ip = {1,2,3,4};
assert(*ip == 1); // true
ip += 2; // adds 4-bytes to the original value of ip
// (2*sizeof(int)) => (2*2) => (4 bytes)
assert(*ip == 3); // true
Cela fonctionne parce que le tableau est juste une liste d'éléments identiques (dans ce cas int
s), disposés séquentiellement dans un seul bloc contigu de mémoire. Le pointeur commence par pointer vers le premier élément du tableau. L'arithmétique du pointeur nous permet ensuite d'avancer le pointeur dans le tableau, élément par élément. Cela fonctionne pour les pointeurs de tout type (sauf l'arithmétique n'est pas autorisé sur void *
).
En fait, c'est exactement la façon dont le compilateur traduit l'utilisation de l'opérateur d'indexeur de tableau []
. C'est littéralement un raccourci pour une addition de pointeur avec un opérateur de déréférencement.
assert(ip[2] == *(ip+2)); // true
Alors, comment tout cela est lié à votre question?
Voici votre configuration ...
char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;
pour l'instant, j'ai simplifié en supprimant l'appel à setStaticAndDynamicPointers
. (Il y a aussi un problème dans cette fonction-donc s'il vous plaît voir la réponse @ Nim, et mon commentaire, pour plus de détails sur la fonction).
char c;
c = (*ppc)[1];
assert(c == 'b'); // assertion doesn't fail.
Cela fonctionne, parce que (*ppc)
dit "me donner tout ce ppc
des points à". C'est l'équivalent de, ppc[0]
. Tout est parfaitement valide.
memcpy(&c,&(*ppc[1]),1);
if(c!='b')
puts("memcpy didn't work."); // this gets printed out.
Les autres problématiques de partie ont fait OUT- est &(*ppc[1])
, qui signifie littéralement pris « me donner un pointeur sur ce qui ppc [1] points. » Tout d'abord, simplifions ... la priorité d'opérateur dit: &(*ppc[1])
est la même que &*ppc[1]
. Alors &
et *
sont inverses et s'annulent mutuellement. Donc, &(*ppc[1])
simplifie à ppc[1]
.
Maintenant, compte tenu de la discussion ci-dessus, nous sommes maintenant à même de comprendre pourquoi cela ne fonctionne pas: En bref, nous traiter ppc
comme si elle pointe vers un tableau de pointeurs, alors qu'en fait, il ne pointe vers un seul pointeur.
Lorsque le compilateur rencontre ppc[1]
, il applique l'arithmétique de pointeur décrite ci-dessus et fournit un pointeur vers la mémoire qui suit immédiatement la variable pc
- tout ce que cette mémoire peut contenir. (Le comportement ici est toujours indéfini). Le problème n'est donc pas du tout avec memcopy()
. Votre appel à memcpy(&c,&(*ppc[1]),1)
copie consciencieusement 1 octet (comme demandé) à partir de la mémoire pointée par le pointeur ppc[1]
et l'écrit dans la variable de caractère c
.
Comme d'autres l'ont souligné, vous pouvez corriger cela en déplaçant votre parenthèse autour de:
memcpy(&c,&((*ppc)[1]),1)
J'espère que l'explication a été utile. Bonne chance!
C ou C++? Beaucoup trop d'appels à la bibliothèque d'exécution C (CRT) et des pointeurs bruts pour que ce soit du code C++, je pense. –
Je ne vois pas de C++ dans cela, donc j'ai enlevé la balise 'C++'. Corrige moi si je me trompe. – sbi
@sbi: Vous avez tort. Le code utilise new [] pour allouer de la mémoire et c'est uniquement du C++. Bien que le nouveau pourrait facilement être remplacé par malloc. –