Come siamo arrivati qui
La sintassi C per la dichiarazione dei punti di funzione mirava a rispecchiare l'utilizzo. Prendi in considerazione una dichiarazione di funzione regolare come questa da <math.h>
:
double round(double number);
Per avere una variabile punto è possibile assegnarla a con sicurezza di tipo usando
fp = round;
avresti dovuto dichiarare la variabile punto fp
in questo modo:
double (*fp)(double number);
Quindi tutto ciò che devi fare è guardare come useresti la funzione e sostituire il nome di quella funzione con un riferimento di puntatore, rendendo round
in *fp
. Tuttavia, hai bisogno di un set extra di parens, che alcuni direbbero un po 'più confuso.
Probabilmente, questo era più facile nel C originale, che non aveva nemmeno la firma della funzione, ma non torniamo indietro, ok?
Il punto in cui diventa particolarmente sgradevole è capire come dichiarare una funzione che accetta come argomento o restituisce un puntatore a una funzione, o entrambe.
Se avevi una funzione:
void myhandler(int signo);
potresti passarlo alla funzione di segnale (3) in questo modo:
signal(SIGHUP, myhandler);
o se vuoi mantenere il vecchio gestore, quindi
old_handler = signal(SIGHUP, new_handler);
che è abbastanza facile. Ciò che è abbastanza semplice - né bello, né facile - è ottenere le dichiarazioni giuste.
signal(int signo, ???)
Bene, si ritorna alla dichiarazione della funzione e si scambia il nome con un punto di riferimento:
signal(int sendsig, void (*hisfunc)(int gotsig));
Poiché non stai dichiarando gotsig
, potresti trovarlo più facile da leggere se ometti:
signal(int sendsig, void (*hisfunc)(int));
O forse no. : (
Tranne che non è abbastanza buono, perché signal (3) restituisce anche il vecchio gestore, come in:
old_handler = signal(SIGHUP, new_handler);
Quindi ora devi capire come dichiarare tutti quelli.
void (*old_handler)(int gotsig);
è sufficiente per la variabile che stai per assegnare. Tieni presente che in realtà non stai dichiarando gotsig
qui, solo old_handler
. Quindi questo è veramente sufficiente:
void (*old_handler)(int);
Questo ci porta ad una definizione corretta per signal (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedef to the Rescue
A quest'ora, penso che tutti saranno d'accordo che è un casino. A volte è meglio dare un nome alle tue astrazioni; spesso, davvero. Con il giusto typedef
, diventa molto più facile da capire:
typedef void (*sig_t) (int);
Ora la variabile del proprio gestore diventa
sig_t old_handler, new_handler;
e la tua dichiarazione per il segnale (3) diventa solo
sig_t signal(int signo, sig_t handler);
che è improvvisamente comprensibile. Liberarsi degli * si sbarazza anche di alcune delle parentesi confuse (e dicono che Parens rende sempre le cose più facili per capire - hah!). Il tuo utilizzo è sempre lo stesso:
old_handler = signal(SIGHUP, new_handler);
ma ora hai la possibilità di comprendere le dichiarazioni per old_handler
, new_handler
e anche signal
quando le incontri per la prima volta o devi scriverle.
Conclusione
Molto pochi programmatori C, si scopre, sono in grado di escogitare le dichiarazioni corrette per queste cose da sole senza consultare i materiali di riferimento.
Lo so, perché una volta avevamo questa stessa domanda sulle nostre domande di intervista per le persone che eseguivano il kernel e il driver del dispositivo. :) Certo, abbiamo perso molti candidati in quel modo mentre si schiantano e bruciano sulla lavagna. Ma abbiamo anche evitato di assumere persone che affermavano di aver avuto esperienze precedenti in quest'area, ma che in realtà non potevano fare il lavoro.
A causa di questa diffusa difficoltà, però, probabilmente non è solo ragionevole, ma anche ragionevole, avere una strada da percorrere per tutte quelle dichiarazioni che non richiedono più di essere un programmatore di geek tripla alfa con tre sigma sopra la media usare questo genere di cose comodamente.