Wie übergibt man eine Funktion als Parameter in C?
616
Ich möchte eine Funktion erstellen, die eine Funktion ausführt, die von einem Parameter für einen Datensatz übergeben wird. Wie übergibt man eine Funktion als Parameter in C?
Der Funktionsname sollte ein Ponter der Funktion sein. Die meisten Leute, die C lernen, decken früher oder später qsort ab, was genau das tut?
McKenzm
5
@MooingDuck Ich stimme Ihrem Vorschlag nicht zu, und Ihrem Vorschlag fehlen Argumente, um die Schlussfolgerung zu stützen. Ich persönlich bevorzuge es, niemals typedef für Funktionszeiger zu verwenden, und ich denke, dies macht den Code klarer und leichter lesbar.
Andrewrk
2
@andrewrk: Du bevorzugst void funcA(void(*funcB)(int))und void (*funcA())()zu typedef void funcB(); void funcA(funcB)und funcB funcA()? Ich sehe den Kopf nicht.
Mooing Duck
Antworten:
747
Erklärung
Ein Prototyp für eine Funktion, die einen Funktionsparameter verwendet, sieht folgendermaßen aus:
void func (void(*f)(int));
Dies besagt, dass der Parameter fein Zeiger auf eine Funktion ist, die einen voidRückgabetyp hat und einen einzelnen intParameter akzeptiert. Die folgende Funktion ( print) ist ein Beispiel für eine Funktion, die funcals Parameter übergeben werden kann, da es sich um den richtigen Typ handelt:
void print (int x ){
printf("%d\n", x);}
Funktionsaufruf
Beim Aufruf einer Funktion mit einem Funktionsparameter muss der übergebene Wert ein Zeiger auf eine Funktion sein. Verwenden Sie dazu den Namen der Funktion (ohne Klammern):
func(print);
würde aufrufen funcund die Druckfunktion an ihn übergeben.
Funktionskörper
Wie bei jedem Parameter funckann jetzt der Name des Parameters im Funktionskörper verwendet werden, um auf den Wert des Parameters zuzugreifen. Angenommen, dies funcwendet die Funktion an, die an die Zahlen 0-4 übergeben wird. Überlegen Sie zunächst, wie die Schleife aussehen würde, um print direkt aufzurufen:
for(int ctr =0; ctr <5; ctr++){
print(ctr);}
Da funcdie Parameterdeklaration besagt, dass dies fder Name für einen Zeiger auf die gewünschte Funktion ist, erinnern wir uns zunächst daran, dass, wenn fes sich um einen Zeiger *fhandelt, auf was gezeigt wird f(dh printin diesem Fall auf die Funktion ). Ersetzen Sie daher einfach jedes Auftreten von Druck in der obigen Schleife durch *f:
In Ihrem ersten und letzten Codebeispiel ist das * nicht obligatorisch. Sowohl die Funktionsparameterdefinition als auch der fFunktionsaufruf können fgenauso wie ohne * ausgeführt werden. Es könnte jedoch eine gute Idee sein, dies so zu tun, wie Sie es tun, um deutlich zu machen, dass der Parameter f ein Funktionszeiger ist. Aber es schadet ziemlich oft der Lesbarkeit.
Gauthier
5
Beispiele finden Sie in [c99, 6.9.1§14]. Beides ist natürlich richtig, ich wollte nur die Alternative erwähnen.
Gauthier
6
"Ja wirklich?" Die am besten bewertete Antwort enthält keinen einzigen Verweis auf die Verwendung eines typedeffor-Funktionszeigers? Entschuldigung, ich muss abstimmen.
Jonathon Reinhart
3
@ JonathonReinhart, was wären die Vorteile von 'typedef' apprach? Diese Version sieht jedoch viel sauberer aus und es fehlen auch zusätzliche Aussagen. so ziemlich Starter hier.
Abhinav Gauniyal
3
@ JonathonReinhart gut entdeckt; Es sollte ausdrücklich darauf hingewiesen werden, dass Zeigertypedefs den Code verschleiern und daher nicht verwendet werden sollten.
MM
129
Diese Frage enthält bereits die Antwort zum Definieren von Funktionszeigern. Sie können jedoch sehr unübersichtlich werden, insbesondere wenn Sie sie in Ihrer Anwendung weitergeben. Um diese Unannehmlichkeit zu vermeiden, würde ich empfehlen, dass Sie den Funktionszeiger in etwas Lesbareres eingeben. Zum Beispiel.
typedefvoid(*functiontype)();
Deklariert eine Funktion, die void zurückgibt und keine Argumente akzeptiert. So erstellen Sie einen Funktionszeiger auf diesen Typ:
Für eine Funktion, die ein int zurückgibt und ein Zeichen annimmt, würden Sie dies tun
typedefint(*functiontype2)(char);
und es zu benutzen
int dosomethingwithchar(char a){return1;}
functiontype2 func2 =&dosomethingwithchar
int result = func2('a');
Es gibt Bibliotheken, die helfen können, Funktionszeiger in gut lesbare Typen umzuwandeln. Die Boost-Funktionsbibliothek ist großartig und die Mühe wert!
Wenn Sie "einen Funktionszeiger in einen Typ verwandeln" möchten, benötigen Sie die Boost-Bibliothek nicht. Verwenden Sie einfach typedef; Es ist einfacher und erfordert keine zusätzlichen Bibliotheken.
Sehr schöne Antwort mit ganzen Codeblöcken (anstatt alles in ein unverständliches Chaos zu zerlegen). Könnten Sie die Unterschiede beider Techniken näher erläutern?
Rafael Eyng
5
Funktionen können gemäß ISO C11 6.7.6.3p8 als Funktionszeiger "übergeben" werden: " Eine Deklaration eines Parameters als" Funktionsrückgabetyp "muss wie in 6.3 als " Zeiger auf Funktionsrückgabetyp "angepasst werden .2.1. ". Zum Beispiel:
Aus welchem Dokument zitieren Sie? Irgendein Link dazu?
Rafael Eyng
1
Ich zitiere aus dem ISO C11-Standard.
Doppelheathen
4
Sie müssen einen Funktionszeiger übergeben . Die Syntax ist etwas umständlich, aber sie ist sehr leistungsfähig, sobald Sie sich damit vertraut gemacht haben.
Es ist nicht wirklich eine Funktion, aber es ist ein lokalisierter Code. Natürlich gibt es den Code nicht nur als Ergebnis weiter. Es funktioniert nicht, wenn es an einen Ereignis-Dispatcher übergeben wird, um zu einem späteren Zeitpunkt ausgeführt zu werden (da das Ergebnis jetzt berechnet wird und nicht, wenn das Ereignis eintritt). Aber es lokalisiert Ihren Code an einem Ort, wenn das alles ist, was Sie versuchen zu tun.
#include<stdio.h>intIncMultInt(int a,int b){
a++;return a * b;}int main(int argc,char*argv[]){int a =5;int b =7;
printf("%d * %d = %d\n", a, b,IncMultInt(a, b));
b =9;// Create some local code with it's own local variable
printf("%d * %d = %d\n", a, b,({int _a = a+1; _a * b;}));return0;}
typedef
.void funcA(void(*funcB)(int))
undvoid (*funcA())()
zutypedef void funcB(); void funcA(funcB)
undfuncB funcA()
? Ich sehe den Kopf nicht.Antworten:
Erklärung
Ein Prototyp für eine Funktion, die einen Funktionsparameter verwendet, sieht folgendermaßen aus:
Dies besagt, dass der Parameter
f
ein Zeiger auf eine Funktion ist, die einenvoid
Rückgabetyp hat und einen einzelnenint
Parameter akzeptiert. Die folgende Funktion (print
) ist ein Beispiel für eine Funktion, diefunc
als Parameter übergeben werden kann, da es sich um den richtigen Typ handelt:Funktionsaufruf
Beim Aufruf einer Funktion mit einem Funktionsparameter muss der übergebene Wert ein Zeiger auf eine Funktion sein. Verwenden Sie dazu den Namen der Funktion (ohne Klammern):
würde aufrufen
func
und die Druckfunktion an ihn übergeben.Funktionskörper
Wie bei jedem Parameter
func
kann jetzt der Name des Parameters im Funktionskörper verwendet werden, um auf den Wert des Parameters zuzugreifen. Angenommen, diesfunc
wendet die Funktion an, die an die Zahlen 0-4 übergeben wird. Überlegen Sie zunächst, wie die Schleife aussehen würde, um print direkt aufzurufen:Da
func
die Parameterdeklaration besagt, dass diesf
der Name für einen Zeiger auf die gewünschte Funktion ist, erinnern wir uns zunächst daran, dass, wennf
es sich um einen Zeiger*f
handelt, auf was gezeigt wirdf
(dhprint
in diesem Fall auf die Funktion ). Ersetzen Sie daher einfach jedes Auftreten von Druck in der obigen Schleife durch*f
:Quelle
quelle
f
Funktionsaufruf könnenf
genauso wie ohne * ausgeführt werden. Es könnte jedoch eine gute Idee sein, dies so zu tun, wie Sie es tun, um deutlich zu machen, dass der Parameter f ein Funktionszeiger ist. Aber es schadet ziemlich oft der Lesbarkeit.typedef
for-Funktionszeigers? Entschuldigung, ich muss abstimmen.Diese Frage enthält bereits die Antwort zum Definieren von Funktionszeigern. Sie können jedoch sehr unübersichtlich werden, insbesondere wenn Sie sie in Ihrer Anwendung weitergeben. Um diese Unannehmlichkeit zu vermeiden, würde ich empfehlen, dass Sie den Funktionszeiger in etwas Lesbareres eingeben. Zum Beispiel.
Deklariert eine Funktion, die void zurückgibt und keine Argumente akzeptiert. So erstellen Sie einen Funktionszeiger auf diesen Typ:
Für eine Funktion, die ein int zurückgibt und ein Zeichen annimmt, würden Sie dies tun
und es zu benutzen
Es gibt Bibliotheken, die helfen können, Funktionszeiger in gut lesbare Typen umzuwandeln. Die Boost-Funktionsbibliothek ist großartig und die Mühe wert!
ist so viel schöner als die oben genannten.
quelle
typedef
; Es ist einfacher und erfordert keine zusätzlichen Bibliotheken.Seit C ++ 11 können Sie die Funktionsbibliothek verwenden , um dies kurz und allgemein zu tun. Die Syntax lautet z.
wo
bool
ist der Rückgabetyp hier eine einargumentigen Funktion , deren ersten Argument vom Typint
.Ich habe unten ein Beispielprogramm eingefügt:
Manchmal ist es jedoch bequemer, eine Vorlagenfunktion zu verwenden:
quelle
Übergeben Sie die Adresse einer Funktion als Parameter an eine andere Funktion, wie unten gezeigt
Wir können die Funktion auch als Parameter mit dem Funktionszeiger übergeben
quelle
Funktionen können gemäß ISO C11 6.7.6.3p8 als Funktionszeiger "übergeben" werden: " Eine Deklaration eines Parameters als" Funktionsrückgabetyp "muss wie in 6.3 als " Zeiger auf Funktionsrückgabetyp "angepasst werden .2.1. ". Zum Beispiel:
ist gleichbedeutend damit:
quelle
Sie müssen einen Funktionszeiger übergeben . Die Syntax ist etwas umständlich, aber sie ist sehr leistungsfähig, sobald Sie sich damit vertraut gemacht haben.
quelle
Es ist nicht wirklich eine Funktion, aber es ist ein lokalisierter Code. Natürlich gibt es den Code nicht nur als Ergebnis weiter. Es funktioniert nicht, wenn es an einen Ereignis-Dispatcher übergeben wird, um zu einem späteren Zeitpunkt ausgeführt zu werden (da das Ergebnis jetzt berechnet wird und nicht, wenn das Ereignis eintritt). Aber es lokalisiert Ihren Code an einem Ort, wenn das alles ist, was Sie versuchen zu tun.
quelle
IncMultInt
?