Funktionsdefinitionen
Eine Funktionsdefinition assoziiert den Funktionsrumpf (eine Sequenz von Deklarationen und Anweisungen) mit dem Funktionsnamen und der Parameterliste. Im Gegensatz zur Funktionsdeklaration sind Funktionsdefinitionen nur im Dateibereich erlaubt (es gibt keine verschachtelten Funktionen).
C unterstützt zwei verschiedene Formen von Funktionsdefinitionen
| attr-spec-seq(optional) specifiers-and-qualifiers parameter-list-declarator function-body | (1) | ||||||||
| specifiers-and-qualifiers identifier-list-declarator declaration-list function-body | (2) | (bis C23) | |||||||
wobei
| attr-spec-seq | - | (C23)eine optionale Liste von Attributen, die auf die Funktion angewendet werden |
| specifiers-and-qualifiers | - | eine Kombination aus
|
| parameter-list-declarator | - | ein Deklarator für einen Funktionstyp, der eine Parameterliste verwendet, um Funktionsparameter zu bezeichnen |
| identifier-list-declarator | - | ein Deklarator für einen Funktionstyp, der eine Identifikatorliste verwendet, um Funktionsparameter zu bezeichnen |
| declaration-list | - | Sequenz von Deklarationen, die jeden Identifikator in identifier-list-declarator deklarieren. Diese Deklarationen dürfen keine Initialisierer verwenden, und der einzige zulässige Speicherklassen-Spezifizierer ist register. |
| function-body | - | eine zusammengesetzte Anweisung, d.h. eine in geschweifte Klammern eingeschlossene Sequenz von Deklarationen und Anweisungen, die jedes Mal ausgeführt wird, wenn diese Funktion aufgerufen wird |
int max(int a, int b) { return a>b?a:b; } double g(void) { return 0.1; }
int max(a, b) int a, b; { return a>b?a:b; } double g() { return 0.1; }
Inhalt |
[bearbeiten] Erläuterung
Wie bei Funktionsdeklarationen muss der Rückgabetyp der Funktion, der durch den Typ-Spezifizierer in specifiers-and-qualifiers bestimmt und möglicherweise durch den declarator wie üblich in Deklarationen modifiziert wird, ein vollständiger Nicht-Array-Objekttyp oder der Typ void sein. Wenn der Rückgabetyp cvr-qualifiziert wäre, wird er zur Konstruktion des Funktionstyps auf seine nicht qualifizierte Version angepasst.
Wie bei Funktionsdeklarationen werden die Typen der Parameter zur Konstruktion des Funktionstyps von Funktionen in Zeiger und von Arrays in Zeiger angepasst, und die Top-Level-cvr-Qualifizierer aller Parametertypen werden zur Bestimmung des kompatiblen Funktionstyps ignoriert.
|
Im Gegensatz zu Funktionsdeklarationen sind unbenannte formale Parameter nicht erlaubt (andernfalls gäbe es Konflikte in alten (K&R) Funktionsdefinitionen); sie müssen benannt werden, auch wenn sie innerhalb der Funktion nicht verwendet werden. Die einzige Ausnahme ist die spezielle Parameterliste (void). |
(bis C23) |
|
Formale Parameter können in Funktionsdefinitionen unbenannt sein, da alte (K&R) Funktionsdefinitionen entfernt wurden. Unbenannte Parameter sind innerhalb des Funktionsrumpfes nicht namentlich zugänglich. |
(seit C23) |
int f(int, int); // declaration // int f(int, int) { return 7; } // Error until C23, OK since C23 int f(int a, int b) { return 7; } // definition int g(void) { return 8; } // OK: void doesn't declare a parameter
Innerhalb des Funktionsrumpfes ist jeder benannte Parameter ein lvalue-Ausdruck; er hat automatische Speicherdauer und Blockgültigkeitsbereich. Das Layout der Parameter im Speicher (oder ob sie überhaupt im Speicher gespeichert werden) ist nicht spezifiziert: Es ist Teil der Aufrufkonvention.
int main(int ac, char **av) { ac = 2; // parameters are lvalues av = (char *[]){"abc", "def", NULL}; f(ac, av); }
Siehe Funktionsaufrufoperator für weitere Details zu den Mechanismen eines Funktionsaufrufs und return für das Zurückkehren aus Funktionen.
__func__Innerhalb jedes function-body ist die spezielle vordefinierte Variable __func__ mit Blockgültigkeitsbereich und statischer Speicherdauer verfügbar, als ob sie unmittelbar nach der öffnenden Klammer definiert wäre durch static const char __func__[] = "function name"; Dieser spezielle Bezeichner wird manchmal in Kombination mit den vordefinierten Makrokonstanten __FILE__ und __LINE__ verwendet, zum Beispiel von assert. |
(seit C99) |
[bearbeiten] Anmerkungen
Die Argumentliste muss im Deklarator explizit vorhanden sein, sie kann nicht von einem typedef geerbt werden.
typedef int p(int q, int r); // p is a function type int(int, int) p f { return q + r; } // Error
|
In C89 war specifiers-and-qualifiers optional, und wenn weggelassen, wurde der Rückgabetyp der Funktion standardmäßig zu int (möglicherweise durch den declarator ergänzt). Darüber hinaus erforderte eine alte Stil-Definition keine Deklaration für jeden Parameter in declaration-list. Jeder Parameter, dessen Deklaration fehlte, hatte den Typ int max(a, b) // a and b have type int, return type is int { return a>b?a:b; } |
(bis C99) |
[bearbeiten] Defect Reports
Die folgenden verhaltensändernden Defect Reports wurden rückwirkend auf zuvor veröffentlichte C-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| DR 423 | C89 | der Rückgabetyp könnte qualifiziert sein | der Rückgabetyp ist implizit nicht qualifiziert |
[bearbeiten] Referenzen
- C17-Standard (ISO/IEC 9899:2018)
- 6.9.1 Funktionsdefinitionen (S. 113-115)
- C11-Standard (ISO/IEC 9899:2011)
- 6.9.1 Funktionsdefinitionen (S. 156-158)
- C99-Standard (ISO/IEC 9899:1999)
- 6.9.1 Funktionsdefinitionen (S. 141-143)
- C89/C90-Standard (ISO/IEC 9899:1990)
- 3.7.1 Funktionsdefinitionen
[bearbeiten] Siehe auch
| C++ Dokumentation für Funktionsdefinition
|