Wie wir hierher gekommen sind
Die C-Syntax zum Deklarieren von Funktionspunkten sollte die Verwendung spiegeln. Betrachten Sie eine reguläre Funktionsdeklaration wie diese von <math.h>
:
double round(double number);
Um eine Punktvariable zu haben, können Sie diese mit der Typensicherheit zuweisen
fp = round;
Sie müssten fp
diese Punktvariable folgendermaßen deklariert haben :
double (*fp)(double number);
Sie müssen sich also nur ansehen, wie Sie die Funktion verwenden würden, und den Namen dieser Funktion durch eine Zeigerreferenz ersetzen, die round
in *fp
. Sie benötigen jedoch einen zusätzlichen Satz von Parens, was einige sagen würden, macht es ein bisschen chaotischer.
Im Original-C, das nicht einmal über eine Funktionssignatur verfügte, war dies früher einfacher, aber lasst uns nicht dorthin zurückkehren, ok?
Besonders unangenehm wird es, wenn man herausfindet, wie eine Funktion deklariert wird, die entweder ein Argument annimmt oder einen Zeiger auf eine Funktion zurückgibt, oder beides.
Wenn Sie eine Funktion hatten:
void myhandler(int signo);
Sie könnten es auf folgende Weise an die Signalfunktion (3) übergeben:
signal(SIGHUP, myhandler);
oder wenn Sie den alten Handler behalten möchten, dann
old_handler = signal(SIGHUP, new_handler);
Das ist ziemlich einfach. Was ziemlich einfach ist - weder hübsch noch einfach -, ist die Richtigkeit der Erklärungen.
signal(int signo, ???)
Sie kehren einfach zu Ihrer Funktionsdeklaration zurück und tauschen den Namen gegen eine Punktreferenz aus:
signal(int sendsig, void (*hisfunc)(int gotsig));
Da Sie nicht deklarieren, gotsig
fällt es Ihnen möglicherweise leichter, zu lesen, wenn Sie Folgendes weglassen:
signal(int sendsig, void (*hisfunc)(int));
Oder vielleicht nicht. :(
Abgesehen davon, dass dies nicht gut genug ist, da das Signal (3) auch den alten Handler zurückgibt, wie in:
old_handler = signal(SIGHUP, new_handler);
Also müssen Sie jetzt herausfinden, wie Sie all diese deklarieren können.
void (*old_handler)(int gotsig);
ist genug für die Variable, der Sie zuweisen werden. Beachten Sie, dass Sie gotsig
hier nur nicht wirklich deklarieren old_handler
. Das ist also wirklich genug:
void (*old_handler)(int);
Das bringt uns zu einer korrekten Definition für signal (3):
void (*signal(int signo, void (*handler)(int)))(int);
Typedefs zur Rettung
Ich denke, zu diesem Zeitpunkt sind sich alle einig, dass das ein Durcheinander ist. Manchmal ist es besser, Ihre Abstraktionen zu benennen. oft wirklich. Mit dem Recht typedef
wird dies viel einfacher zu verstehen:
typedef void (*sig_t) (int);
Jetzt wird Ihre eigene Handlervariable
sig_t old_handler, new_handler;
und Ihre Deklaration für Signal (3) wird gerecht
sig_t signal(int signo, sig_t handler);
das ist plötzlich nachvollziehbar. Wenn Sie die * loswerden, werden Sie auch einige der verwirrenden Klammern los (und sie sagen, dass Parens die Dinge immer leichter verständlich machen - hah!). Ihre Nutzung ist immer noch die gleiche:
old_handler = signal(SIGHUP, new_handler);
aber jetzt haben Sie Chance, die Erklärungen zu verstehen , für old_handler
, new_handler
und selbst signal
wenn man sie zuerst oder Notwendigkeit begegnen , sie zu schreiben.
Fazit
Es stellt sich heraus, dass nur sehr wenige C-Programmierer in der Lage sind, die richtigen Deklarationen für diese Dinge selbst zu erstellen, ohne Referenzmaterialien zu konsultieren.
Ich weiß, weil wir diese Frage einmal in unseren Interviewfragen für Leute hatten, die Kernel- und Gerätetreiberarbeiten erledigen. :) Sicher, wir haben viele Kandidaten auf diese Weise verloren, als sie auf dem Whiteboard abgestürzt und verbrannt sind. Wir haben aber auch vermieden, Leute einzustellen, die behaupteten, sie hätten bereits Erfahrungen in diesem Bereich gesammelt, konnten diese Arbeit aber nicht ausführen.
Aufgrund dieser weit verbreiteten Schwierigkeit ist es jedoch wahrscheinlich nicht nur sinnvoll, sondern auch vernünftig, über all die Erklärungen zu verfügen, für die Sie nicht länger ein Triple-Alpha-Geek-Programmierer sein müssen, der drei Sigmas über dem Durchschnitt sitzt, nur um diesen zu verwenden so etwas bequem.
f :: (Int -> Int -> Int) -> Int -> Int