Domanda

Ci sono le seguenti dichiarazioni:

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

Poi, da qualche parte nel programma v'è la seguente chiamata:

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(Ignorate i primi tre argomenti e numeric).

chiedo che cosa è questa:

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

Capisco che qsort si aspetta un "puntatore a funzione che riceve due puntatori void e restituisce un int" come è quarto argomento, ma come ciò che è scritto sopra soddisfa che? Mi sembra come una sorta di fusione perché è fatta di due parentesi, ma che sarebbe un cast molto strano. Perché ci vuole una funzione e rende la funzione di un "puntatore a funzione che riceve due puntatori void e restituisce un int". Il che non ha senso.
(Ho seguito qui la regola che un tipo di type tra parentesi prima di una variabile promuove la variabile di quel tipo).

Quindi penso che ottengo solo sbagliato, forse qualcuno può dirmi come leggere questo, qual è l'ordine?

È stato utile?

Soluzione

Quello che sta succedendo qui è davvero un cast. Consente di ignorare il ternario per un secondo e far finta che numcmp viene sempre utilizzato. Ai fini di questa domanda, le funzioni possono agire come puntatori a funzione in C. Quindi, se si guarda al tipo di numerico in realtà è

(int (*)(int*,int*))

Affinché questo sia adeguatamente utilizzato in qsort ha bisogno di avere parametri vuoto. Poiché i tipi qui tutti hanno la stessa dimensione rispetto ai parametri e tipi restituiti, è possibile sostituire il per l'altro. Tutto ciò che serve è un cast per rendere il compilatore felice.

(int (*)(void*,void*))(numcmp )

Altri suggerimenti

Hai perso il trucco - la parte

(numeric ? numcmp : strcmp)

sta usando l'operatore ternario di scegliere che funzione viene chiamata all'interno di qsort. Se i dati sono numerici, utilizza numcmp. In caso contrario, utilizza strcmp. Un'implementazione più leggibile sarebbe simile a questa:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

È possibile farlo senza il cast puntatore a funzione. Ecco come . Nella mia esperienza, nella maggior parte dei luoghi, se si utilizza un cast, che si sta facendo male.

Si noti che la definizione standard di qsort() comprende const:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Si noti che il comparatore stringa è dato due valori 'char **', non valori "char *.

scrivo i miei comparatori in modo che calchi sono inutili nel codice chiamante:

#include <stdlib.h>    /* qsort() */
#include <string.h>    /* strcmp() */

int num_cmp(const void *v1, const void *v2)
{
    int i1 = *(const int *)v1;
    int i2 = *(const int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

int str_cmp(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return(strcmp(s1, s2));
}

Costringere le persone a scrivere calchi nel codice utilizzando le funzioni è brutto. Non farlo.

Le due funzioni che ho scritto corrisponde al prototipo di funzione richiesta dal qsort() standard. Il nome di una funzione quando non seguito da parentesi è equivalente a un puntatore alla funzione.

Troverete in vecchio codice, o codice scritto da parte di coloro che sono stati portati su compilatori più anziani, che puntatori a funzioni vengono utilizzate con la notazione:

result = (*pointer_to_function)(arg1, arg2, ...);

In stile moderno, che è scritto:

result = pointer_to_function(arg1, arg2, ...);

Personalmente, trovo la dereferenziare esplicita più chiara, ma non tutti sono d'accordo.

Chi ha scritto che frammento di codice stava cercando di essere troppo intelligente . Nella sua mente, probabilmente pensa di essere un buon programmatore, facendo un intelligente "one-liner". In realtà, sta facendo il codice che è meno leggibile ed è antipatico a lavorare con sopra il lungo termine e deve essere riscritto in una forma più evidente simile al codice di Harper Shelby.

Ricordate l'adagio da Brian Kernighan:

  

Il debugging è due volte più duro come la scrittura   il codice in primo luogo.   Pertanto, se si scrive il codice come   abilmente possibile, sei, da   definizione, non abbastanza intelligente per eseguire il debug   esso.


Faccio un sacco di prestazioni di codifica critico con scadenze hard real time ... e non ho ancora visto un luogo dove una fitta one-liner è appropriato.

Ho anche pasticciato con la compilazione e verifica della asm per vedere se l'one-liner dispone di un'implementazione asm meglio compilato, ma non ho mai trovato l'one-liner per essere valsa la pena.

Come altri hanno fatto notare, per

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

allora il seguente è un cast di tipo

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

e l'espressione è

(numeric ? numcmp : strcmp)

dichiarazioni C può essere molto difficile da leggere, ma è possibile imparare. Il metodo è quello di iniziare in corrispondenza della parte interna e poi a destra un passo, poi a sinistra un passo, continuando a destra, sinistra, destra, sinistra, ecc verso l'esterno fino al termine. Non si attraversa fuori una parentesi prima di tutto dentro è stata valutata. Ad esempio per il tipo di getto sopra, (*) indica un puntatore. Pointer era l'unica cosa all'interno delle parentesi così poi valutiamo al lato destro fuori di esso. (void*,void*) indica che è un puntatore a una funzione con due argomenti puntatore. Infine int indica il tipo di ritorno della funzione. La parentesi esterna rende questo un tipo di cast. Aggiornamento: due articoli più dettagliati: Il senso orario / spirale Regola e lettura noreferrer dichiarazioni C:. Una guida per la Mystified

Tuttavia, la buona notizia è che, anche se quanto sopra è estremamente utile per conoscere, non è un modo estremamente semplice per ingannare : cdecl programma può convertire da C a inglese descrizione e viceversa:

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

Esercizio: Che tipo di variabile è i

int *(*(*i)[])(int *)

rot13 nel caso in cui non si dispone di cdecl installato sulla vostra macchina (ma si dovrebbe davvero):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

Vorrei probabilmente letto in questo modo:

typedef int (*PFNCMP)(void *, void *);

PFNCMP comparison_function;

if (numeric)
{
    comparison_function =  numcmp;
}
else
{
    comparison_function = strcmp;
}

qsort((void**) lineptr, 0, nlines-1, comparison_function);

L'esempio in questione ha un caso esplicito.

La logica è corretta credo. Si è infatti casting per "puntatore a funzione che riceve due puntatori void e restituisce un int", che è il tipo di richiesta dal firma del metodo.

Sia numcmp e strcmp sono puntatori a funzioni che prendono due char* come parametri e restituisce un int. La routine qsort previsto un puntatore a una funzione che prende due void* come parametri e restituisce un int. Quindi il cast. Questo è sicuro, dal momento che void* agisce come un puntatore generico. Ora, per la lettura della dichiarazione: Prendiamo tuo la dichiarazione del strcmp:

 int strcmp(char *, char *);

Il compilatore legge come strcmp in realtà è:

 int (strcmp)(char *, char *)

una funzione (decomposizione a un puntatore a una funzione nella maggior parte dei casi), che ha due argomenti char *. Il tipo di puntatore strcmp è quindi:

 int (*)(char *, char *)

Quindi, quando si ha bisogno di lanciare un'altra funzione per essere compatibile a strcmp devi utilizzare il sopra come il type per lanciare a.

Allo stesso modo, dal momento che l'argomento comparatore di qsort prende due void *s e quindi il cast strano!

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