Як ми тут потрапили
Синтаксис C для оголошення функціональних точок був призначений для відображення дзеркального використання. Розглянемо звичайну декларацію функції на зразок цього <math.h>
:
double round(double number);
Щоб мати змінну точки, ви можете призначити її за допомогою безпеки типу
fp = round;
вам потрібно було б оголосити цю fp
змінну точки таким чином:
double (*fp)(double number);
Так що все , що вам потрібно зробити , це подивитися на те, як ви будете використовувати цю функцію, і замініть ім'я цієї функції з посиланням покажчика, що робиш round
в *fp
. Однак вам потрібен додатковий набір паронів, який, як кажуть деякі, робить його трохи заплутанішим.
Можливо, це було простіше в оригіналі C, який навіть не мав підпису функції, але не будемо повертатися туди, добре?
Місце, яке йому стає особливо неприємним, - це з'ясування того, як оголосити функцію, яка або бере аргумент, або повертає вказівник на функцію, або і те і інше.
Якщо у вас була функція:
void myhandler(int signo);
ви можете передати його до функції сигналу (3) таким чином:
signal(SIGHUP, myhandler);
або якщо ви хочете зберегти старий обробник, то
old_handler = signal(SIGHUP, new_handler);
що досить легко. Що досить легко - ні красиво, ні просто - це правильне декларування.
signal(int signo, ???)
Ну, ви просто повертаєтесь до своєї декларації функції і поміняєте ім'я на посилання на точку:
signal(int sendsig, void (*hisfunc)(int gotsig));
Оскільки ви не заявляєте gotsig
, вам може бути легше прочитати, якщо ви не пропустите:
signal(int sendsig, void (*hisfunc)(int));
А може, ні. :(
За винятком того, що це недостатньо добре, оскільки сигнал (3) також повертає старий обробник, як у:
old_handler = signal(SIGHUP, new_handler);
Тож тепер ви повинні розібратися, як заявити про все це.
void (*old_handler)(int gotsig);
достатньо для змінної, якій ви збираєтесь призначити. Зауважте, що ви насправді gotsig
тут не декларуєте old_handler
. Тож цього справді достатньо:
void (*old_handler)(int);
Це приводить нас до правильного визначення сигналу (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedefs до рятувального
До цього часу я думаю, що всі погодиться, що це безлад. Іноді краще назвати свої абстракції; часто, справді. З правом typedef
це стає набагато простіше зрозуміти:
typedef void (*sig_t) (int);
Тепер ваша змінна обробка обробника стає
sig_t old_handler, new_handler;
і ваша заява на сигнал (3) стає справедливою
sig_t signal(int signo, sig_t handler);
що раптом зрозуміло. Позбавлення від * також позбудеться деяких заплутаних дужок (і вони кажуть, що парени завжди полегшують розуміння - так!). Ваше використання все одно те саме:
old_handler = signal(SIGHUP, new_handler);
але тепер у вас є шанс зрозуміти декларації для old_handler
, new_handler
і навіть signal
коли ви вперше зіткнетеся з ними або вам потрібно їх написати.
Висновок
Дуже мало програмістів на C, виявляється, здатні самостійно розробити правильні декларації для цих речей без консультацій з довідковими матеріалами.
Я знаю, бо колись у нас на запитання про інтерв'ю у нас виникало саме це питання для людей, які виконують роботу драйвера ядра та пристрою. :) Звичайно, ми втратили дуже багато кандидатів, коли вони розбилися і спалили на дошці. Але ми також уникали найму людей, які заявляли, що мали попередній досвід роботи в цій галузі, але насправді не могли виконати роботу.
Через цю поширену складність, мабуть, не просто розумно, але справді є розумним розглянути всі декларації, які більше не вимагають від вас, щоб програміст потрійних альфа-вундеркіндів сидів на три сигми вище середнього просто для використання цього щось зручно.
f :: (Int -> Int -> Int) -> Int -> Int