2010-02-09 5 views
8

J'essaie d'échanger des objets pour un problème de devoirs qui utilise des pointeurs vides pour permuter des objets. La déclaration de ma fonction doit être:Permutation d'objets à l'aide de pointeurs

void swap(void *a, void *b, size_t size); 

Je ne cherche pas le code exact comment le faire, je peux le comprendre par moi-même, mais je ne suis pas sûr si je comprends bien. J'ai trouvé qu'un problème est en faisant:

void *temp; 
temp = a; 
a = b; 
b = temp; 

ne change que ce que les pointeurs pointent vers. Est-ce exact? Si c'est correct, pourquoi les pointeurs d'échange ne changent-ils pas le contenu entre * a et * b? Parce que si votre pointeur pointe vers quelque chose de différent, ne pourriez-vous pas le déréférencer et les objets seraient maintenant différents?

De même, commutant les valeurs comme:

void *temp; 
*temp = *a; 
*a = *b; 
*b = *temp; 

n'est pas correct non plus, que je ne sais pas pourquoi. Car encore une fois, il me semble que le contenu est basculé. Est-ce que les objets échangés signifient un échange complet de la mémoire et la valeur de ce vers quoi pointe un pointeur?

Donc il me semble que je dois utiliser malloc pour allouer assez d'espace pour mon swap. Si j'attribue assez de mémoire pour un objet, en supposant qu'ils ont la même taille, je ne vois pas vraiment en quoi il est différent des deux autres méthodes ci-dessus.

void *temp = malloc(sizeof(pa)); 
// check for null pointer 
temp = a; 
// do something I'm not sure of since I don't quite get how allocating space is any 
// different than the two above methods??? 

Merci!

+1

Vous êtes proche. Déférer un 'void *' n'est pas autorisé cependant. Pensez à d'autres façons de déplacer l'information, en particulier au paramètre 'size' et à l'effet que cela a sur votre algorithme. –

+0

Modifier les pointeurs est un bon essai, mais ne fonctionnera pas car ils ont été passés en valeur. Ainsi, l'appelant aura la même valeur dans les pointeurs avant l'appel. –

Répondre

19

pointeurs Permutation ne change pas les valeurs pointues à. Si c'était le cas, ce serait comme échanger des étiquettes d'adresse sur des enveloppes qui m'emmèneraient dans votre maison et vous dans la mienne.

Vous étiez presque là:

void swap(void *a, void *b, size_t size) { 
    char temp[size]; // C99, use malloc otherwise 
    // char serves as the type for "generic" byte arrays 

    memcpy(temp, b, size); 
    memcpy(b, a, size); 
    memcpy(a, temp, size); 
} 

La mémoire des copies de fonction memcpy, ce qui est la définition des objets en C. (. Appelée POD ou des données « ol ordinaire en C++, pour comparer) De cette façon, memcpy est de savoir comment vous faites mission sans se soucier du type de l'objet, et vous pouvez même écrire d'autres missions comme memcpy à la place:

int a = 42, b = 3, temp; 

temp = b; 
b = a; 
a = temp; 
// same as: 
memcpy(&temp, &b, sizeof a); 
memcpy(&b, &a, sizeof a); 
memcpy(&a, &temp, sizeof a); 

Ceci est exactement ce que la fonction ci-dessus ne, puisque vous ne pouvez pas utiliser l'affectation lorsque vous faites ne pas connaître le type de l'objet, et v oid est le type qui correspond à "inconnu". (Cela signifie également "rien" lorsqu'il est utilisé comme type de retour de fonction.)


Comme une curiosité, une autre version qui évite malloc dans les cas courants et ne pas utiliser VLA de C99:

void swap(void *a, void *b, size_t size) { 
    enum { threshold = 100 }; 
    if (size <= threshold) { 
    char temp[threshold]; 

    memcpy(temp, b, size); 
    memcpy(b, a, size); 
    memcpy(a, temp, size); 
    } 
    else { 
    void* temp = malloc(size); 
    assert(temp); // better error checking desired in non-example code 

    memcpy(temp, b, size); 
    memcpy(b, a, size); 
    memcpy(a, temp, size); 

    free(temp); 
    } 
} 
+1

Bonne idée, mais c'est memcpy (dest, src, size); –

+0

+1 pour éviter les tentations de 'alloca'! –

+0

@Earwicker: Si je vais utiliser quelque chose qui n'est pas en C89, je préférerais que ce soit C99 plutôt que alloca.:) –

0

Pour changer le pointeur à l'intérieur et le garder à l'extérieur, vous devez passer le pointeur par référence ou un double pointeur.

Si votre fonction doit être comme:

void swap(void *a, void *b, size_t size); 

Je suppose que vous devez mettre en œuvre quelque chose comme:

void * temp; 
temp = malloc(size); 
memcpy(temp,a,size); 
memcpy(a,b,size); 
memcpy(b,temp,size); 
free(temp); 
1

D'abord, notez que toute modification des pointeurs à l'intérieur la La fonction ne sera pas propagée en dehors de la fonction. Donc, vous allez devoir déplacer la mémoire.

La meilleure façon de le faire est de memcpy - allouer un tampon sur la pile, memcpy la taille appropriée de a en elle, memcpyb-a, et une dernière memcpy de température dans b.

0

Pour avoir un effet réel, vous devez faire l'équivalent du deuxième bloc vous avez mentionné:

void *temp; 
*temp = *a; 
*a = *b; 
*b = *temp; 

Le problème ici est que « vide » n'a pas de taille, de sorte que vous ne pouvez pas assigner 'void's comme ils sont. Vous devez allouer de l'espace pour temp pour pointer sur, puis copier les valeurs en utilisant quelque chose comme memcpy().

+0

cant deref void * – pm100

+0

@ pm100 - il explique en fait cela dans la réponse. –

+1

@ pm100: Contrairement à un compilateur, vous devez faire attention à la fois au code * et * aux commentaires! –

2

Les paramètres sont comme des variables locales, avec des valeurs copiées avant que la fonction ne commence à s'exécuter. Ce prototype:

void swap(void *a, void *b, size_t size); 

signifie que les deux adresses sont copiées dans de nouvelles variables appelées a et b. Donc, si vous modifiez ce qui est stocké dans a et b, rien de ce que vous faites n'aura un effet après les retours swap.

1

Si vous écriviez une fonction pour permuter deux nombres entiers, en leur donnant des pointeurs, votre solution de permutation des valeurs pointées fonctionnerait. Cependant, considérer la situation avec

struct { 
    int a; 
    int b; 
} a, b; 

swap(&a, &b, sizeof(a)); 

Vous devez trouver un moyen d'échanger le contenu de chaque valeur passée sans aucune connaissance de ce qu'ils consistent en fait.

3

Pour répondre à votre première question, nous allons combler certaines valeurs pour voir ce qui se passe:

void* a = 0x00001000; // some memory address 
void* b = 0x00002000; // another memory address 
/* Now we'll put in your code */ 
void* temp; // temp is garbage 
temp = a; // temp is now 0x00001000 
a = b; // a is now 0x00002000 
b = temp; // b is now 0x00001000 

Ainsi, à la fin de ces déclarations, les valeurs du pointeur ont été échangés contre, c'est, quel que soit a pointait à est maintenant pointé par b, et vice versa. Les valeurs de ce que pointent ces pointeurs ne sont pas modifiées, c'est juste que maintenant leurs adresses mémoire sont maintenues par des pointeurs différents.

Pour répondre à votre deuxième question, vous ne pouvez pas déréférencer un void*. La raison en est que void n'a pas de taille, donc essayer et déréférencer ou assigner quelque chose qui n'a pas de taille est absurde. Ainsi, void* est un moyen de garantir que vous pouvez pointer vers quelque chose, mais vous ne saurez jamais ce que quelque chose est sans plus d'informations (d'où le paramètre size à votre routine).À partir de là, connaissant le pointeur et la taille des données pointées par le pointeur, vous pouvez utiliser une routine comme memcpy pour déplacer les données pointées par un pointeur dans l'emplacement pointé par un autre.

1

Vous êtes proche.

Le problème est: vous « swapping » seulement des pointeurs un et b qui sont des variables locales dans la fonction.

Je suppose en dehors de la fonction que vous avez certaines variables, nous allons les appeler:

void *x = ...; 
void *y = ...; 

Lorsque vous appelez:

swap(x, y, some_size); 

un et b points aux mêmes objets que x et et respectivement. Maintenant, quand vous échangez ce que un et b des points aussi, x et y pointent toujours à ce qu'ils où pointant avant.

Pour changer ce mémoire x et y points que vous devez passer un pointeur vers la variable x , donc un pointeur vers un pointeur :)

Parce que vous ne pouvez pas modifier la déclaration de fonction vous ne pouvez échanger le contenu de la mémoire où x (et un) et y (et b) points. Certaines solutions sont dans d'autres réponses :) Généralement memcpy est ce que vous voulez.

2

J'avais une question similaire à celle-ci pour mon cours C. Je pense que memcopy est probablement mieux, mais vous pouvez aussi essayer ceci:

typedef unsigned char * ucp; 

    void swap(void *a, void *b, int size){ 
     ucp c=(ucp)a; 
     ucp d=(ucp)b; 
     for(int i=0; i<size; i++){ 
     int temp=(int)c[i]; 
    c[i]=(int)d[i]; 
    d[i]=temp; 
     } 

    } 

Fondamentalement, ce que cela fait est jeté deux pointeurs à un type de pointeur unsigned char. Ensuite, vous incrémentez le pointeur qui, dans le cas d'un caractère non signé, incrémente un BYTE à la fois. Alors ce que vous faites est fondamentalement la copie du contenu de chaque octet à la fois dans la mémoire. Si quelqu'un veut corriger ou clarifier cela, je l'apprécierais aussi.

0

Il ne faut pas utiliser memcpy pour échanger deux pointeurs, le code suivant fonctionne bien (testé pour la permutation int * et char * cordes):

void swap(void **p, void **q) 
{ 
    void *t = *p; 
    *p = *q; 
    *q = t; 
}