2011-02-08 3 views
1

J'essaye de créer une liste liée où vous pouvez mettre à jour les données dans un noeud, mais peu importe ce que j'essaie, C ne semble pas me permettre de mettre à jour la valeur d'un pointeur vide (ou plutôt où il pointe à). Voici le code de test je:Comment modifier un pointeur pour annuler une fonction?

void newData(void * d) 
{ 
    char data[] = "world"; 

    d = &data; 
} 

int main() 
{ 
     char testData[] = "hello"; 
     void * testPointer = &testData; 

     printf("TestData is %s\n", (char *)testPointer); 

     // Modify the data 
     newData(&testPointer); 

     printf("TestData is %s\n", (char *)testPointer); 
} 

qui produit juste ::

TestData is hello 
TestData is hello 

Suis-je raté quelque chose d'évident? J'ai également essayé d'utiliser un pointeur sur un pointeur, mais en vain.

+2

Le type d'argument est erroné, il devrait être vide ** . –

+0

Ou simplement l'appeler comme 'newData (testPointer); '? – John

+0

'void * testPointer = & testData;' L'opérateur d'adresse n'est pas requis dans cette instruction. Lorsque vous affectez un tableau à un pointeur, l'adresse du tableau est affectée. Donc, à la place, vous pouvez faire 'void * testPointer = testData;'. Il existe d'autres instructions dans votre programme où vous pouvez le faire. – Mahesh

Répondre

4

Deux choses sont mal à cela:

char data[] = "world"; 

est-probablement créé dans le cadre du cadre de pile de la fonction. Les tableaux se dégradent en pointeurs. En tant que tel, lorsque la fonction appelle ret, il aurait dû nettoyer sa pile et la mémoire à cette adresse a disparu. Si une opération fonctionne ici, c'est parce que vous n'avez pas encore écrasé la mémoire.

Que pourriez-vous faire? Déclarez-le static est une solution qui garantit (selon c99 au moins) l'existence du programme-vie (c'est-à-dire qu'elle ne sera pas allouée sur la pile mais dans le segment de données, et la bibliothèque c l'alloue avant main). Cependant, comme je soupçonne que c'est juste une démo, il convient de souligner que:

char data[] = "world"; 
memcpy(d, data, 5); 

est parfaitement valide, puisque vous copiez le contenu et ne pointent pas vers des valeurs.

newData(&testPointer); 

Vous faites une erreur simple ici. Un pointeur, dans l'assemblage, est une adresse mémoire contenant une autre adresse mémoire. Lorsque vous passez un pointeur sur une fonction, vous voulez passer cette adresse mémoire, de sorte que lorsque vous appelez cette fonction, le contenu du pointeur, une adresse mémoire, est copié sur la pile sous la forme d'un nouveau pointeur. Bien sûr, ces deux pointeurs pointent vers la même chose, qui est la façon dont vous finissez par atteindre une chose de type passer-par-référence. Si vous ne me croyez pas, regardez-le dans un débogueur. Cependant, ce que vous faites est de passer l'adresse d'un pointeur, ainsi vous créez un pointeur vers un pointeur vers une valeur. Imaginez la mémoire comme ceci:

|Address|Value 
|0x120 |0x121 <-- this is what you're passing with &testPointer 
|0x121 |0x122 <-- this is a pointer; it contains the address of a value 
|0x122 |h  <-- this is a value. 
|0x123 |e 
|0x124 |l 
... 

J'espère que cela le rendra plus clair. Dans ma mémoire simpliste, vous passez 0x120 plutôt que 0x121. Vous pouvez bien sûr déréférencer deux fois, mais pourquoi? La solution simple est juste pour passer le pointeur comme ceci:

newData(testPointer); 
+0

Comme expliqué dans un commentaire à une autre réponse, la variable locale 'data' est sur la pile, mais "world \ 0" existe dans le segment de données du programme, donc data [] = "world" est valide. – user47559

+0

Une autre façon d'y penser est: 'data' est une variable locale sur la pile, mais elle pointe vers un emplacement qui n'est pas sur la pile. – user47559

7

Je pense que vous avez besoin

void newData(void ** d) 
{ 
    char data[] = "world"; 
    *d = &data; 
}  

Cependant, cela a ses propres problèmes, comme « monde » est pile locale, et ne sera pas valide après votre retour de newData.

+2

Cependant, je pense que cela fonctionnerait s'il le changeait en const char * data = "world"; comme "monde" serait alors une chaîne codée en dur dans le binaire. – TheBuzzSaw

+0

Cela signifie-t-il que je dois allouer un peu plus d'espace pour une nouvelle valeur, ou y a-t-il un moyen plus simple? Je veux simplement changer la chaîne à l'intérieur de testPointer. – Lewis

+2

@Lewis: il n'y a pas de chaîne pointeur de test "inside". Les chaînes de style C ne fonctionnent pas comme ça. La chaîne de style C est essentiellement un pointeur vers le premier caractère d'une séquence de caractères. Donc, cette séquence de caractères doit exister d'une manière ou d'une autre. Lorsque vous affectez un littéral à un pointeur char ('char * data =" world "'), la séquence char contenant '" world \ 0 "' est créée par le compilateur implicitement et stockée dans les données du programme. L'affectation affecte donc simplement l'adresse des données précédemment allouées à la variable. – Vlad

0

Je pense qu'un peu de changement est le code peut résoudre le problème:

void newData(void ** d) 
{ 
    char* data = "world"; 

    *d = data; 
} 
Questions connexes