Was bedeuten die Klammern um einen Funktionsnamen?

214

In einer meiner Projektquelldateien habe ich diese C-Funktionsdefinition gefunden:

int (foo) (int *bar)
{
    return foo (bar);
}

Hinweis: Neben befindet sich kein Sternchen foo, daher handelt es sich nicht um einen Funktionszeiger. Oder ist es? Was ist hier mit dem rekursiven Aufruf los?

user1859094
quelle
7
Nein, es ist kein Funktionszeiger - es ist immer noch eine reguläre Funktion namens foo.
Nemanja Boric
Ist das die komplette Funktion?
Asheeshr
2
Haben Sie Beweise dafür, dass diese Funktion in einem nützlichen Kontext verwendet wird?
Moooeeeep
1
... sieht aus wie eine Dummy-Funktion, die vielleicht nur geschrieben wurde, um zu sehen, ob sie in einer vorhandenen Quelle kompiliert wird und hätte entfernt werden sollen. Ich würde es entfernen (wenn es das ist, was die Funktion wirklich tut), da es bestenfalls eine Endlosschleife ist (ich bin nicht sicher, ob der C-Compiler diesen Tail-Aufruf zum Springen optimieren darf), im schlimmsten Fall einen Stapelüberlauf.
Hyde
3
Klammern in C-Deklarationen helfen dabei, die Sprache mehrdeutig zu machen. Schnell, was ist a(b);? Deklaration bals Variable des Typs a? Oder ein Aufruf, amit Argumenten zu funktionieren b? Der Unterschied ist syntaktisch, und Sie können nicht wissen, wie Sie ihn überhaupt analysieren können, ohne die Deklarationsinformationen von nachzuschlagen a. Dies sind die Klammern für Postfix-Funktionsaufrufe oder optionale Klammern um einen Deklarator.
Kaz

Antworten:

329

Wenn keine Präprozessor-Inhalte vorhanden sind, entspricht foodie Signatur der

int foo (int *bar)

Der einzige Kontext, in dem ich gesehen habe, wie Leute scheinbar unnötige Klammern um Funktionsnamen setzen, ist, wenn sowohl eine Funktion als auch ein funktionsähnliches Makro mit demselben Namen vorhanden sind und der Programmierer die Makroerweiterung verhindern möchte.

Diese Vorgehensweise mag zunächst etwas seltsam erscheinen, aber die C-Bibliothek stellt einen Präzedenzfall dar, indem sie einige Makros und Funktionen mit identischen Namen bereitstellt .

Ein solches Funktions / Makro-Paar ist isdigit(). Die Bibliothek kann es wie folgt definieren:

/* the macro */
#define isdigit(c) ...

/* the function */
int (isdigit)(int c) /* avoid the macro through the use of parentheses */
{
  return isdigit(c); /* use the macro */
}

Ihre Funktion sieht fast identisch mit der oben genannten aus, daher vermute ich, dass dies auch in Ihrem Code vor sich geht.

NPE
quelle
2
Das kann auch hier der Fall sein; Ich habe nicht nach Makros gesucht ... Und ich wusste nicht, dass die Makroerweiterung nicht in Klammern stattfindet. Vielen Dank, dass Sie darauf hingewiesen haben!
user1859094
13
@ user1859094: Auf den zweiten Blick ist dies mit ziemlicher Sicherheit das, was in Ihrem Code vor sich geht. Das foo(bar)Innere der Funktion verwendet das entsprechende Makro.
NPE
78
@ user1859094 Die Makroerweiterung erfolgt zwar in Klammern, die Erweiterung eines funktionsähnlichen Makros erfolgt jedoch nur, wenn das nächste Token eine linke Klammer ist (C99, 6.10.3§10) und somit foo (int* bar)ersetzt wird, jedoch nicht (foo) (int *bar)(das nächste Token) nach fooist ))
Virgile
4
Wie würde eine solche Funktion heißen? Würden Sie es auch mit den Klammern nennen? Würde das zum Beispiel funktionieren : (isdigit)(5)?
Gcochard
4
@ Greg: Richtig, genau so würdest du es nennen.
NPE
37

Die Klammern ändern die Deklaration nicht - sie definieren immer noch nur eine gewöhnliche Funktion, die aufgerufen wird foo.

Der Grund, warum sie verwendet wurden, ist mit ziemlicher Sicherheit, dass es ein funktionsähnliches Makro namens foodefined gibt:

#define foo(x) ...

Die Verwendung (foo)in der Funktionsdeklaration verhindert, dass dieses Makro hier erweitert wird. Was also wahrscheinlich passiert, ist, dass eine Funktion foo()definiert wird, wobei ihr Körper aus dem funktionsähnlichen Makro erweitert wird foo.

caf
quelle
5
Netter Abzug (obwohl die Verwendung von Klammern für diesen Zweck gesetzlich strafbar sein sollte).
Ugoren
3
@ugoren: Die Verwendung von Parens um den Funktionsnamen ist die einzige Möglichkeit, eine Makroerweiterung für ein funktionsähnliches Makro zu verhindern. Manchmal ist es ein notwendiges Werkzeug.
Michael Burr
7
@MichaelBurr, es gibt auch die Möglichkeit, kein Makro und keine Funktion mit demselben Namen zu haben. Ich weiß, dass Sie nicht immer alles kontrollieren können, aber wenn Sie zu dieser Lösung gelangen, würde ich sagen, dass etwas sehr falsch ist.
Ugoren
-3

Die Klammern sind bedeutungslos.
Der Code, den Sie anzeigen, ist nichts anderes als eine unendliche Rekursion.

Wenn Sie einen Funktionszeiger definieren, sehen Sie manchmal seltsame Klammern, die etwas bedeuten. Dies ist hier jedoch nicht der Fall.

ugoren
quelle
6
Offensichtlich nicht; Die Klammern verhindern die Makroerweiterung. Siehe die akzeptierte Antwort.
Kevin
12
@ Kevin, meine Antwort bezieht sich auf den angezeigten Code und ist dafür richtig. In fast jeder C-Frage kann die Annahme unbekannter Präprozessordefinitionen alles ändern. In diesem Fall sind Antworten, die den Präprozessor berücksichtigen, zwar besser, machen meine aber nicht falsch.
Ugoren