Domanda

Questo proviene da una libreria di array "magica" che sto usando.

void
sort(magic_list *l, int (*compare)(const void **a, const void **b))
{
    qsort(l->list, l->num_used, sizeof(void*),
         (int (*)(const void *,const void *))compare);
}

La mia domanda è: cosa diavolo sta facendo l'ultimo argomento a Qsort?

(int (*)(const void *, const void*))compare) 

Qsort prende int (*comp_fn)(const void *,const void *) Poiché è l'argomento del comparatore, ma questa funzione di tipo prende un comparatore con doppi puntatori. In qualche modo, la riga sopra converte la versione a doppio puntatore in una versione a singolo puntatore. Qualcuno può aiutare a spiegare?

È stato utile?

Soluzione

Sulla maggior parte dell'hardware puoi supporre che tutti i puntatori abbiano tutti uguali a livello hardware. Ad esempio, in un sistema con puntatori a 64 bit piatti sarà sempre una quantità intera a 64 bit. Lo stesso vale per i puntatori di puntatori o puntatori a puntatori a puntatori ai puntatori.

Pertanto, qualunque metodo sia usato per invocare una funzione con due puntatori funzionerà con qualsiasi funzione che richieda due puntatori. Il tipo specifico di puntatori non ha importanza.

qsort Tratta i puntatori genericamente, come se ognuno fosse opaco. Quindi non sa né si preoccupa di come siano derenziati. Sa in quale ordine si trovano attualmente e usa l'argomento di confronto per capire in quale ordine dovrebbero essere.

La biblioteca che stai usando presumibilmente mantiene elenchi di puntatori su cui puntatori. Ha una funzione di confronto che può confrontare due puntatori con i puntatori. Quindi lo lancia per passare a Qsort. È solo sintatticamente più bello di, ad esempio

qsort(l->list, l->num_used, sizeof(void*), compare);

/* elsewhere */

int compare(const void *ptr1, const void *ptr2)
{
    // these are really pointers to pointers, so cast them across
    const void **real_ptr1 = (const void **)ptr1;
    const void **real_ptr2 = (const void **)ptr2;

    // do whatever with real_ptr1 and 2 here, e.g.
    return (*real_ptr2)->sort_key - (*real_ptr1)->sort_key;
}

Altri suggerimenti

Questo è esattamente quello che fa il cast che hai citato: converte un puntatore di tipo

int (*)(const void **, const void **)

a un puntatore di tipo

int (*)(const void *, const void *)

Quest'ultimo è ciò che ci si aspetta da qsort.

Cosa si incontrano piuttosto spesso in codice di qualità negativa. Ad esempio, quando qualcuno vuole ordinare una serie di ints, scrivono spesso una funzione di confronto che accetta i suggerimenti int *

int compare_ints(const int *a, const int *b) {
  return (*a > *b) - (*a < *b);
}

E quando arriva il momento di chiamare effettivamente qsort Lo hanno lanciato con forza al tipo corretto per sopprimere le lamentele del compilatore

qsort(array, n, sizeof *array, (int (*)(const void *,const void *)) compare_ints);

Questo è un "hack", che porta a comportamenti indefiniti. È, ovviamente, una cattiva pratica. Quello che vedi nel tuo esempio è solo una versione meno diretta dello stesso "hack".

L'approccio corretto in tali casi sarebbe quello di dichiarare la funzione di confronto come

int compare_ints(const void *a, const void *b) {
  int a = *(const int *) a;
  int b = *(const int *) b;
  return (a > b) - (a < b);
}

e poi usalo senza cast

qsort(array, n, sizeof *array, compare_ints);

In generale, se ci si aspetta che le loro funzioni di confronto vengano utilizzate come comparatori in qsort (e funzioni simili), si dovrebbe implementare con const void * parametri.

L'ultimo argomento a Qsort è lanciare un puntatore di funzione che prende doppi puntatori, a uno che prenderà singoli puntatori che Qsort accetterà. È semplicemente un cast.

Sta lanciando un puntatore di funzione. Immagino che il motivo sia che il confronto possa essere applicato ai puntatori che sono derenziati piuttosto che qualunque cosa stiano indicando.

(int (*)(const void *,const void *))compare è un cast di stile C per lanciare il puntatore della funzione compare a un puntatore di funzione con due const void * args.

L'ultimo argomento è un puntatore della funzione. Specifica che impiega un puntatore a una funzione che restituisce un INT e ne prende due const void ** argomenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top