2016-05-30 42 views
8

Ho difficoltà a capire perché un pezzo di codice funziona. La seguente è una funzione di confronto per l'attuazione di stdlib di qsort:Problemi con i puntatori e i puntatori ai puntatori

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = (char *) p1; 
    v2 = (char *) p2; 
    return strcmp(v1,v2); 
} 

Naturalmente, questo funziona solo per le stringhe. La mia domanda è: perché funziona il codice qui sotto?

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

Mi sembra che nella seconda versione, sto con forza scaccio ciò è chiaramente un char*-char**. Il problema è che la variabile conterrà ancora un indirizzo in una variabile char. Quando applico *, è a mia conoscenza che C elaborerà questo comando recuperando il contenuto di p1 e quindi leggendo 8 byte (sul mio arco) seguendo l'indirizzo memorizzato all'interno, in modo che alla fine ottenga un valore di tipo char*. Questo dovrebbe, a mio parere, portare a unire 8 caratteri in un indirizzo di memoria non valido.

Ancora, entrambe le funzioni funzionano altrettanto bene. Dove sto andando male?

+1

La seconda versione sembra corretto a me.Difficile dire come funziona la prima versione senza vedere il resto del codice. –

+1

come si chiama la funzione? Se chiami la funzione come 'scmp (" ciao "," ciao ");' solo la prima versione funzionerà: http://ideone.com/P96Wmj – mch

+1

Potrebbe sembrare che funzioni in alcuni casi ma non lavoro per tutti i casi. Se per esempio p1 punta alla stringa "abcdefgh" e p2 ad un'altra stringa "abcdefgh". Ora le stringhe sono uguali e sono entrambe interpretate come lo stesso indirizzo (chiamiamolo p). Quindi strcmp confronterà la stringa in p con la stringa in p, e poiché entrambi i parametri puntano allo stesso indirizzo, i contenuti sono per definizione uguali. –

risposta

6

Supponiamo di voler ordinare un array di int s utilizzando qsort.

int numbers[] = {10, 50, 35, 62, 22}; 

In primo luogo, si crea una funzione che può confrontare due int s.

int intCompare(void* p1, void* p2) 
{ 
    int n1 = *(int*)p1; 
    int n2 = *(int*)p2; 
    return (n1 < n2); 
} 

Quindi, è possibile utilizzare:

qsort(numbers, 5, sizeof(int), intCompare); 

Quando numbers viene passato al qsort, è decaduto a un int* e passato come void*. Quando abbiamo bisogno di estrarre il numero da un void* in intCompare, dobbiamo lanciarlo su int* prima di dereferenziare il puntatore e confrontare i valori.

Prendendo l'analogia con le stringhe, diciamo che si desidera ordinare:

char* strings[] = { "abc", "xyz", "def" }; 

La chiamata a qsort saranno:

qsort(strings, 3, sizeof(char*), scmp); 

Quando strings è passato a qsort, si è decaduto a un char** e passati come void*. I tipi sottostanti dei puntatori passati a scmp da qsort saranno di tipo char**, non char*. Quindi, è corretto usare:

int scmp(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

La prima versione funziona a causa di una coincidenza fortunata in alcuni casi. Ecco un esempio di programma che mostra un paio di casi in cui non funziona mentre la seconda versione dovrebbe sempre funzionare.

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

// First version of scmp 
int scmp1(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = (char *) p1; 
    v2 = (char *) p2; 
    return strcmp(v1,v2); 
} 

// Second version of scmp 
int scmp2(const void *p1, const void * p2) 
{ 
    char *v1, *v2; 

    v1 = *(char **) p1; 
    v2 = *(char **) p2; 
    return strcmp(v1,v2); 
} 

void test1() 
{ 
    char* strings[] = { "abc", "xyz", "def" }; 
    qsort(strings, 3, sizeof(char*), scmp1); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 
    printf("\n"); 
} 

void test2() 
{ 
    char* strings[] = { "abc", "xyz", "def" }; 
    qsort(strings, 3, sizeof(char*), scmp2); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 
    printf("\n"); 
} 

void test3() 
{ 
    char** strings = malloc(3*sizeof(char*)); 
    strings[0] = "abc"; 
    strings[1] = "xyz"; 
    strings[2] = "def"; 

    qsort(strings, 3, sizeof(char*), scmp1); 
    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 

    free(strings); 
    printf("\n"); 
} 

void test4() 
{ 
    char** strings = malloc(3*sizeof(char*)); 
    strings[0] = "abc"; 
    strings[1] = "xyz"; 
    strings[2] = "def"; 

    qsort(strings, 3, sizeof(char*), scmp2); 

    for(int i = 0; i < 3; ++i) 
    { 
     printf("%s\n", strings[i]); 
    } 

    free(strings); 
    printf("\n"); 
} 

int main() 
{ 
    // Does not work. 
    test1(); 

    // Should work always. 
    test2(); 

    // Does not work. 
    test3(); 

    // Should work always. 
    test4(); 
} 

uscita (usando GCC 4.8.4):

abc 
xyz 
def 

abc 
def 
xyz 

abc 
xyz 
def 

abc 
def 
xyz 
+1

Bello vedere alcune risposte molto dettagliate. +1 – Mirakurun

+0

Adesso ha senso - grazie! – user1123530