2010-11-01 7 views
0

En fait, memcpy fonctionne très bien lorsque j'utilise des pointeurs vers des caractères, mais cesse de fonctionner lorsque j'utilise des pointeurs pour pointer vers des caractères. Quelqu'un peut-il m'aider s'il vous plaît à comprendre pourquoi Memcpy échoue ici, ou mieux encore, comment je pourrais avoir compris moi-même. Je trouve très difficile de comprendre les problèmes qui se posent dans mon code c/C++.memcpy échoue mais l'affectation ne se fait pas sur les pointeurs de caractères

char *pc = "abcd"; 
char **ppc = &pc; 
char **ppc2 = &pc; 
setStaticAndDynamicPointers(ppc, ppc2); 

char c; 
c = (*ppc)[1]; 
assert(c == 'b');      // assertion doesn't fail. 

memcpy(&c,&(*ppc[1]),1); 

if(c!='b') 
    puts("memcpy didn't work."); // this gets printed out. 

c = (*ppc2)[3]; 
assert(c=='d');      // assertion doesn't fail. 
memcpy(&c, &(*ppc2[3]), 1); 

if(c != 'd') 
    puts("memcpy didn't work again."); 

memcpy(&c, pc, 1); 
assert(c == 'a'); // assertion doesn't fail, even though used memcpy 

void setStaticAndDynamicPointers(char **charIn, char **charIn2) 
{ 
    // sets the first arg to a pointer to static memory. 
    // sets the second arg to a pointer to dynamic memory. 
    char stat[5]; 
    memcpy(stat, "abcd", 5); 
    *charIn = stat; 

    char *dyn = new char[5]; 
    memcpy(dyn, "abcd", 5); 
    *charIn2 = dyn; 
} 
+3

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. –

+0

Je ne vois pas de C++ dans cela, donc j'ai enlevé la balise 'C++'. Corrige moi si je me trompe. – sbi

+1

@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. –

Répondre

0

char stat[5];

est une variable de pile qui est hors de portée, il est pas// sets the first arg to a pointer to static memory.. Vous avez besoin de malloc/new de mémoire qui obtient l'abcd mis dedans. Comme vous le faites pour charIn2

4

votre commentaire implique que char stat[5] devrait être statique, mais ce n'est pas le cas. Par conséquent charIn pointe vers un bloc qui est alloué sur la pile, et lorsque vous revenez de la fonction, il est hors de portée. Vouliez-vous dire static char stat[5]?

+0

oui, et dans ce cas particulier, il est difficile de voir l'effet de ce problème, car 'charIn == ppc == & pc' et' charIn2 == ppc2 = & pc'. Donc, ils pointent tous deux vers le même endroit; les deux affectations sont déréférencées, '* charIn = stat' et' * charIn2 = dyn'. Et donc le résultat final est que pc == * charIn == * charIn2 == dyn'. Je ne pense pas que ce soit ce que le PO avait l'intention de faire, mais cela finit par obscurcir le problème de l'allocation statique par opposition à l'allocation de pile. – Lee

0

Juste comme ce que dit Preet, je ne pense pas que le problème est avec memcpy. Dans votre fonction "setStaticAndDynamicPointers", vous définissez un pointeur vers une variable automatique créée sur la pile de cet appel de fonction. Au moment où la fonction se termine, la mémoire pointée par la variable "stat" n'existera plus. Par conséquent, le premier argument ** charIn pointera vers quelque chose de non existant. Vous pouvez peut-être lire plus en détail le cadre de pile (ou l'enregistrement d'activation) ici: link text

Vous avez effectivement créé un pointeur flottant sur une variable de pile dans ce code. Si vous voulez tester les valeurs de copie dans une pile var, assurez-vous qu'elle a été créée dans la fonction d'appel, et non dans la fonction appelée.

0

En plus de la définition de 'stat', le problème principal à mes yeux est que *ppc[3] n'est pas le même que (*ppc)[3]. Ce que vous voulez est ce dernier (le quatrième caractère de la chaîne pointée par ppc), mais dans votre memcpy() s vous utilisez le premier, le premier caractère de la quatrième chaîne dans le "tableau de chaînes" ppc (évidemment ppc n'est pas un tableau de char*, mais vous forcez le compilateur à le traiter comme tel).

Lors du débogage de tels problèmes, je trouve généralement utile d'imprimer les adresses mémoire et le contenu impliqué.

0

Bien que les réponses précédentes soulèvent des arguments valables, je pense que l'autre chose que vous devez regarder est votre règles de priorité de l'opérateur lorsque vous memcpy:

memcpy(&c, &(*ppc2[3]), 1); 

Qu'est-ce qui se passe ici? Ce n'est peut-être pas ce que vous avez l'intention de faire. La notation de tableau takes higher precedence que l'opérateur de déréférencement, de sorte que vous essayez d'abord effectuer arithmétique pointeur équivalent à ppc2++. Vous déréférencez ensuite cette valeur et passez l'adresse dans memcpy. Ce n'est pas la même chose que(*ppc2)[1]. Le résultat sur ma machine est une erreur de violation d'accès (XP/VS2005), mais en général c'est un comportement indéfini. Toutefois, si vous déréférencez la même manière que précédemment:

memcpy(&c, &((*ppc2)[3]), 1); 

Ensuite, cette violation d'accès disparaît et j'obtiens des résultats corrects.

0

Notez que les parenthèses dans les expressions de vos instructions d'affectation se trouvent à des emplacements différents de la parenthèse dans les expressions memcpy. Donc ce n'est pas trop surprenant qu'ils fassent des choses différentes.

1

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!

+0

Je remettrais l'enfer hors de cela, mais hélas, je n'en ai qu'un ... – GJTorikian

+0

@GJTorikian - merci! Je suis content que vous l'ayez trouvé utile. – Lee

Questions connexes