Come siamo arrivati qui
La sintassi C per dichiarare i punti funzione era intesa a rispecchiare l'utilizzo. Considera una dichiarazione di funzione regolare come questa da <math.h>
:
double round(double number);
Per avere una variabile punto è possibile assegnarla a con la sicurezza del tipo utilizzando
fp = round;
dovresti aver dichiarato quella fp
variabile punto 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 a puntatore, trasformandolo round
in *fp
. Tuttavia, è necessario un set aggiuntivo di parentesi, che alcuni direbbero che lo rende un po 'più disordinato.
Probabilmente, questo era più facile nella C originale, che non aveva nemmeno la firma della funzione, ma non torniamo lì, ok?
Il posto in cui diventa particolarmente brutto è capire come dichiarare una funzione che prende come argomento o restituisce un puntatore a una funzione, o entrambi.
Se avessi 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, allora
old_handler = signal(SIGHUP, new_handler);
che è abbastanza facile. Ciò che è abbastanza facile - né carino, né facile - è ottenere le dichiarazioni giuste.
signal(int signo, ???)
Bene, torni semplicemente alla tua dichiarazione di funzione e scambia il nome con un riferimento di punto:
signal(int sendsig, void (*hisfunc)(int gotsig));
Poiché non stai dichiarando gotsig
, potresti omettere di leggere se ometti:
signal(int sendsig, void (*hisfunc)(int));
O forse no. :(
Tranne che non è abbastanza buono, perché il segnale (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 si intende assegnare. Nota che non stai dichiarando davvero gotsig
solo qui old_handler
. Quindi questo è davvero abbastanza:
void (*old_handler)(int);
Questo ci porta a una definizione corretta per segnale (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedefs to the Rescue
A questo punto, penso che tutti saranno d'accordo sul fatto che è un casino. A volte è meglio nominare le tue astrazioni; spesso, davvero. Con il diritto typedef
, questo diventa molto più facile da capire:
typedef void (*sig_t) (int);
Ora diventa la tua variabile gestore
sig_t old_handler, new_handler;
e la tua dichiarazione per il segnale (3) diventa giusta
sig_t signal(int signo, sig_t handler);
che è improvvisamente comprensibile. Sbarazzarsi di * s anche sbarazzarsi di alcune delle parentesi confuse (e dicono che i genitori rendono sempre le cose più facili da capire - ah!). Il tuo utilizzo è sempre lo stesso:
old_handler = signal(SIGHUP, new_handler);
ma ora si ha la possibilità di comprendere le dichiarazioni per old_handler
, new_handler
e anche signal
la prima volta che loro o necessità incontrano di scriverli.
Conclusione
Molto pochi i programmatori C, si scopre, sono in grado di elaborare le dichiarazioni corrette per queste cose da soli senza consultare i materiali di riferimento.
Lo so, perché una volta avevamo questa stessa domanda sulle domande del nostro colloquio per le persone che facevano il kernel e il driver del dispositivo. :) Certo, abbiamo perso molti candidati in quel modo quando si sono schiantati e bruciati sulla lavagna. Ma abbiamo anche evitato di assumere persone che sostenevano di avere precedenti esperienze in questo settore ma che in realtà non potevano svolgere il lavoro.
A causa di questa diffusa difficoltà, tuttavia, probabilmente non è solo ragionevole, ma in effetti ragionevole avere un modo per fare tutte quelle dichiarazioni che non richiedono più di essere un programmatore geek triplo alfa con tre sigmi sopra la media solo per usare questo una specie di cosa comodamente.
f :: (Int -> Int -> Int) -> Int -> Int