Bagaimana Kami Sampai di Sini
Sintaks C untuk menyatakan titik fungsi dimaksudkan untuk mencerminkan penggunaan. Pertimbangkan deklarasi fungsi reguler seperti ini dari <math.h>
:
double round(double number);
Untuk memiliki variabel titik Anda dapat menetapkan bahwa dengan menggunakan jenis keamanan
fp = round;
Anda harus mendeklarasikan fp
variabel titik itu dengan cara ini:
double (*fp)(double number);
Jadi yang harus Anda lakukan adalah melihat bagaimana Anda akan menggunakan fungsi tersebut, dan ganti nama fungsi itu dengan referensi pointer, membuatnya round
menjadi *fp
. Namun, Anda memerlukan seperangkat paren tambahan, yang menurut beberapa orang membuatnya agak berantakan.
Bisa dibilang, ini dulu lebih mudah di C asli, yang bahkan tidak memiliki fungsi tanda tangan, tapi jangan kembali ke sana, ok?
Tempat yang menjadi sangat jahat adalah mencari tahu cara mendeklarasikan fungsi yang diambil sebagai argumen atau mengembalikan pointer ke fungsi, atau keduanya.
Jika Anda memiliki fungsi:
void myhandler(int signo);
Anda bisa meneruskannya ke fungsi sinyal (3) dengan cara ini:
signal(SIGHUP, myhandler);
atau jika Anda ingin mempertahankan pawang lama, maka
old_handler = signal(SIGHUP, new_handler);
yang cukup mudah. Apa yang cukup mudah - atau tidak cantik, tidak mudah - adalah mendapatkan deklarasi yang benar.
signal(int signo, ???)
Nah, Anda cukup kembali ke deklarasi fungsi dan bertukar nama untuk referensi titik:
signal(int sendsig, void (*hisfunc)(int gotsig));
Karena Anda tidak mendeklarasikan gotsig
, Anda mungkin merasa lebih mudah dibaca jika Anda menghilangkan:
signal(int sendsig, void (*hisfunc)(int));
Atau mungkin tidak. :(
Kecuali itu tidak cukup baik, karena sinyal (3) juga mengembalikan handler lama, seperti pada:
old_handler = signal(SIGHUP, new_handler);
Jadi sekarang Anda harus mencari cara untuk mendeklarasikan semua itu.
void (*old_handler)(int gotsig);
sudah cukup untuk variabel yang akan Anda tetapkan. Perhatikan bahwa Anda tidak benar-benar mendeklarasikan di gotsig
sini, hanya old_handler
. Jadi ini sudah cukup:
void (*old_handler)(int);
Itu membawa kita ke definisi yang benar untuk sinyal (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedefs to the Rescue
Pada saat ini, saya pikir semua orang akan setuju bahwa itu berantakan. Terkadang lebih baik memberi nama abstraksi Anda; sering, sungguh. Dengan benar typedef
, ini menjadi lebih mudah untuk dipahami:
typedef void (*sig_t) (int);
Sekarang variabel handler Anda menjadi
sig_t old_handler, new_handler;
dan deklarasi Anda untuk sinyal (3) menjadi adil
sig_t signal(int signo, sig_t handler);
yang tiba-tiba bisa dipahami. Menyingkirkan tanda * juga menghilangkan beberapa tanda kurung yang membingungkan (dan mereka mengatakan bahwa orangtua selalu membuat segala sesuatu lebih mudah untuk dipahami - hah!). Penggunaan Anda masih sama:
old_handler = signal(SIGHUP, new_handler);
tapi sekarang Anda memiliki kesempatan untuk memahami deklarasi untuk old_handler
, new_handler
, dan bahkan signal
ketika Anda pertama kali bertemu dengan mereka atau kebutuhan untuk menulis mereka.
Kesimpulan
Ternyata sangat sedikit programmer C yang mampu merancang deklarasi yang benar untuk hal-hal ini sendiri tanpa berkonsultasi dengan bahan referensi.
Saya tahu, karena kami pernah memiliki pertanyaan tentang pertanyaan wawancara ini untuk orang-orang yang melakukan pekerjaan kernel dan driver perangkat. :) Tentu, kami kehilangan banyak kandidat seperti itu karena mereka jatuh dan terbakar di papan tulis. Tetapi kami juga menghindari mempekerjakan orang yang mengklaim bahwa mereka memiliki pengalaman sebelumnya di bidang ini tetapi sebenarnya tidak dapat melakukan pekerjaan itu.
Karena kesulitan yang meluas ini, mungkin tidak hanya masuk akal tetapi juga masuk akal untuk membahas semua deklarasi yang tidak lagi mengharuskan Anda menjadi programmer geek triple-alpha dengan tiga sigma di atas rata-rata hanya untuk menggunakan ini. semacam itu dengan nyaman.
f :: (Int -> Int -> Int) -> Int -> Int