2010-02-09 12 views
8

Sto provando a scambiare gli oggetti per un problema di compiti a casa che utilizza i puntatori void per scambiare gli oggetti. La dichiarazione di mia funzione deve essere:Scambio di oggetti usando i puntatori

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

non sto cercando il codice esatto come farlo così posso capirlo da solo, ma non sono sicuro se ho capito bene. Ho riscontrato che un problema è il seguente:

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

modifica solo ciò a cui puntano i puntatori. È corretto? Se è corretto, perché lo scambio di puntatori non cambia effettivamente il contenuto tra * a e * b. Perché se il tuo puntatore punta a qualcosa di diverso, non potresti dereferenziarlo e gli oggetti ora sarebbero diversi?

Allo stesso modo, solo il passaggio dei valori come:

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

non è corretto o, che io non so perché. Perché di nuovo, mi sembra che il contenuto sia cambiato.

Scambiare oggetti significa scambiare completamente la memoria e il valore di ciò a cui punta un puntatore?

Quindi sembra che devo usare malloc per allocare abbastanza spazio per il mio scambio. Se alloco abbastanza memoria per un oggetto, supponendo che abbiano le stesse dimensioni, non vedo davvero come sia diverso dagli altri due metodi sopra.

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??? 

Grazie!

+1

Stai vicino. Tuttavia, il differimento di un 'void *' non è permesso. Pensa ad altri modi in cui potresti spostare le informazioni, in particolare pensa al parametro 'size' e all'effetto che ha sul tuo algoritmo. –

+0

Cambiare i puntatori è un bel tentativo, ma non funzionerà perché sono stati passati per valore. Pertanto, il chiamante avrà lo stesso valore nei puntatori prima della chiamata. –

risposta

19

puntatori Scambio di non modificare i valori punta-to. Se lo facesse, sarebbe come scambiare le etichette degli indirizzi sulle buste che mi portano in casa tua e tu nella mia.

eri quasi arrivato:

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); 
} 

Il copie memoria memcpy funzione, che è la definizione di oggetti in C. (. Chiamato dati pianura ol' POD o in C++, di confrontare) In questo modo, memcpy è come fare l'assegnazione, senza preoccuparsi del tipo di oggetto, e si potrebbe anche scrivere altri incarichi come memcpy invece:

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); 

Questo è esattamente ciò che la funzione di cui sopra fa, dal momento che non è possibile utilizzare l'assegnazione quando si fanno non conosco il tipo di oggetto, e v oid è il tipo che sta per "sconosciuto". (Significa anche "nulla" quando usato come tipo di ritorno della funzione.)


Come curiosità, un'altra versione che evita malloc nei casi più comuni e non fa uso di C99 VLA:

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

Giusta idea, ma è memcpy (dest, src, size); –

+0

+1 per evitare le tentazioni di 'alloca'! –

+0

@Earwicker: Se userò qualcosa non in C89, preferirei che fosse C99 che alloca.:) –

0

Per modificare il puntatore all'interno e tenerlo all'esterno, è necessario passare il puntatore per riferimento o un doppio puntatore.

Se la funzione deve essere come:

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

suppongo che si deve implementare qualcosa di simile:

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

Prima di tutto, si noti che eventuali modifiche ai puntatori all'interno la la funzione non verrà propagata all'esterno della funzione. Quindi dovrai spostare la memoria.

Il modo più semplice per farlo è con memcpy - allocare un buffer in pila, memcpy le dimensioni appropriate da a in esso, memcpy da b a a, e un'ultima memcpy dalla temperatura in b.

0

avere alcun effetto reale, è necessario fare l'equivalente del secondo blocco lei ha citato:

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

Il problema qui è che 'vuoto' non ha una dimensione, così non si può assegnare i "vuoti" così come stanno. È necessario allocare lo spazio per temp al punto, quindi copiare i valori utilizzando qualcosa come memcpy().

+0

cant deref void * – pm100

+0

@ pm100 - in realtà lo spiega nella risposta. –

+1

@ pm100: A differenza di un compilatore, ci si aspetta che presti attenzione sia al codice * che ai * commenti! –

2

I parametri sono come variabili locali, con valori copiati prima che la funzione inizi l'esecuzione. Questo prototipo:

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

significa che i due indirizzi vengono copiate in nuove variabili denominate a e b. Quindi, se si modifica ciò che è memorizzato in a e b, nulla di ciò che si avrà avrà alcun effetto dopo i ritorni swap.

1

Se stavate scrivendo una funzione per scambiare due numeri interi, con i relativi puntatori, la soluzione di scambio dei valori puntati avrebbe funzionato. Tuttavia, considerare la situazione con

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

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

È necessario trovare un modo per scambiare il contenuto di ogni valore passato senza alcuna conoscenza di quello che effettivamente consistono.

3

Per rispondere alla tua prima domanda, cerchiamo di riempire alcuni valori per vedere cosa sta succedendo:

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 

Così, alla fine di tali dichiarazioni, i valori del puntatore sono stati scambiati, vale a dire, tutto ciò a stava indicando to è ora indicato da b e viceversa. I valori di di ciò a cui puntano i puntatori non sono modificati, è solo che ora i loro indirizzi di memoria sono trattenuti da diversi indicatori.

Per rispondere alla seconda domanda, non è possibile annullare la chiamata a void*. Il motivo per questo è che void non ha dimensioni, quindi provare e dereferenziare o assegnare a qualcosa che non ha dimensioni è privo di senso. Pertanto, void* è un modo per garantire di poter puntare a qualcosa di, ma non si saprà mai che quello qualcosa di è senza ulteriori informazioni (da cui il parametro size alla routine).

Da lì, conoscendo il puntatore e la dimensione dei dati a cui punta il puntatore, è possibile utilizzare una routine come memcpy per spostare i dati puntati da un puntatore nella posizione indicata da un altro.

1

Sei vicino.

Il problema è: si sta "swapping" solo i puntatori un e b che sono variabili locali nella funzione.

suppongo di fuori della funzione si dispone di alcune variabili, chiamiamoli così:

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

Quando si chiama:

swap(x, y, some_size); 

un e b punti agli stessi oggetti x e y rispettivamente. Ora, quando si scambia quello un e b punti troppo, x e y puntino ancora a quello che in cui puntando prima.

Per cambiare ciò memoria x e y punti si dovrebbe passare un puntatore alla variabile x , quindi un puntatore a un puntatore :)

Poiché non è possibile modificare dichiarazione di funzione è possibile scambiare solo il contenuto della memoria dove x (e un) e y (e b) punti. Alcune soluzioni sono in altre risposte :) Generalmente memcpy è quello che vuoi.

2

Ho avuto una domanda simile a questa per il mio corso C. Penso memcopy è probabilmente migliore, ma si può anche provare questo:

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; 
     } 

    } 

Fondamentalmente ciò che fa è gettato entrambi i puntatori a un tipo di puntatore char senza segno. Quindi si incrementa il puntatore, che nel caso di un carattere non firmato, incrementa un BYTE alla volta. Quindi quello che stai facendo è fondamentalmente copiare i contenuti di ogni byte alla volta in memoria. Se qualcuno vuole correggere o chiarire questo, lo apprezzerei anche io.

0

Non abbiamo bisogno di usare memcpy per scambiare due puntatori, seguente codice funziona bene (testato per lo swapping int * e char * stringhe):

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