Funktionsdeklarationen
Eine Funktionsdeklaration führt einen Bezeichner ein, der eine Funktion bezeichnet, und gibt optional die Typen der Funktionsparameter an (das Prototyp). Funktionsdeklarationen (im Gegensatz zu Definitions) können im Block-Scope sowie im Datei-Scope erscheinen.
Inhalt |
[bearbeiten] Syntax
In der Deklarationsgrammatik einer Funktionsdeklaration bezeichnet die Sequenz von type-specifier, möglicherweise modifiziert durch den Deklarator, den Rückgabetyp (der jeder Typ außer Array- oder Funktionstyp sein kann), und der declarator hat eine von drei Formen
noptr-declarator ( parameter-list ) attr-spec-seq(optional) |
(1) | ||||||||
noptr-declarator ( identifier-list ) attr-spec-seq(optional) |
(2) | (bis C23) | |||||||
noptr-declarator ( ) attr-spec-seq(optional) |
(3) | ||||||||
wobei
| noptr-declarator | - | jeder Deklarator außer einem nicht geklammerten Zeigerdeklarator. Der Bezeichner, der in diesem Deklarator enthalten ist, ist der Bezeichner, der zum Funktionsbezeichner wird. |
| parameter-liste | - | entweder das einzelne Schlüsselwort void oder eine durch Kommas getrennte Liste von Parametern, die mit einem Ellipsenparameter enden kann |
| identifier-list | - | durch Kommas getrennte Liste von Bezeichnern, nur möglich, wenn dieser Deklarator als Teil einer Funktionsdefinition im alten Stil verwendet wird |
| attr-spec-seq | - | (C23)eine optionale Liste von Attributen, die auf den Funktionstyp angewendet werden |
int max(int a, int b); // declaration int n = max(12.01, 3.14); // OK, conversion from double to int
int max(a, b) int a, b; // definition expects ints; the second call is undefined { return a > b ? a : b; } int n = max(true, (char)'a'); // calls max with two int args (after promotions) int n = max(12.01f, 3.14); // calls max with two double args (after promotions)
[bearbeiten] Erklärung
Der Rückgabetyp der Funktion, bestimmt durch den Typspezifizierer in specifiers-and-qualifiers und möglicherweise modifiziert durch den declarator, wie üblich in Deklarationen, muss ein Nicht-Array-Objekttyp oder der Typ void sein. Wenn die Funktionsdeklaration keine Definition ist, kann der Rückgabetyp unvollständig sein. Der Rückgabetyp kann nicht cvr-qualifiziert sein: Jeder qualifizierte Rückgabetyp wird zur Konstruktion des Funktionstyps auf seine unqualifizierte Version angepasst.
void f(char *s); // return type is void int sum(int a, int b); // return type of sum is int. int (*foo(const void *p))[3]; // return type is pointer to array of 3 int double const bar(void); // declares function of type double(void) double (*barp)(void) = bar; // OK: barp is a pointer to double(void) double const (*barpc)(void) = barp; // OK: barpc is also a pointer to double(void)
Funktionsdeklaratoren können mit anderen Deklaratoren kombiniert werden, solange sie ihre Typspezifizierer und Qualifizierer teilen können
int f(void), *fip(), (*pfi)(), *ap[3]; // declares two functions and two objects inline int g(int), n; // Error: inline qualifier is for functions only typedef int array_t[3]; array_t a, h(); // Error: array type cannot be a return type for a function
Wenn eine Funktionsdeklaration außerhalb einer Funktion erscheint, hat der von ihr eingeführte Bezeichner Dateigeltungsbereich und externe Bindung, es sei denn, static wird verwendet oder eine frühere statische Deklaration ist sichtbar. Wenn die Deklaration innerhalb einer anderen Funktion auftritt, hat der Bezeichner Blockgeltungsbereich (und auch entweder interne oder externe Bindung).
int main(void) { int f(int); // external linkage, block scope f(1); // definition needs to be available somewhere in the program }
Die Parameter in einer Deklaration, die nicht Teil einer Funktionsdefinition ist(bis C23) müssen nicht benannt werden
int f(int, int); // declaration // int f(int, int) { return 7; } // Error: parameters must be named in definitions // This definition is allowed since C23
Jeder Parameter in einer parameter-list ist eine Deklaration, die eine einzelne Variable einführt, mit den folgenden zusätzlichen Eigenschaften
- der Bezeichner im Deklarator ist optional (außer wenn diese Funktionsdeklaration Teil einer Funktionsdefinition ist)(bis C23)
int f(int, double); // OK int g(int a, double b); // also OK // int f(int, double) { return 1; } // Error: definition must name parameters // This definition is allowed since C23
- der einzige Speicherklassenspezifizierer, der für Parameter zulässig ist, ist
register, und er wird in Funktionsdeklarationen, die keine Definitionen sind, ignoriert
int f(static int x); // Error int f(int [static 10]); // OK (array index static is not a storage class specifier)
- jeder Parameter vom Array-Typ wird an den entsprechenden Zeigertyp angepasst, der qualifiziert sein kann, wenn sich Qualifizierer zwischen den eckigen Klammern des Array-Deklarators befinden(seit C99)
int f(int[]); // declares int f(int*) int g(const int[10]); // declares int g(const int*) int h(int[const volatile]); // declares int h(int * const volatile) int x(int[*]); // declares int x(int*)
- jeder Parameter vom Funktionstyp wird an den entsprechenden Zeigertyp angepasst
int f(char g(double)); // declares int f(char (*g)(double)) int h(int(void)); // declares int h(int (*)(void))
- die Parameterliste kann mit
, ...enden oder...sein(seit C23), siehe variadische Funktionen für Details.
int f(int, ...);
- Parameter dürfen nicht den Typ void haben (können aber den Typ Zeiger auf void haben). Die spezielle Parameterliste, die ausschließlich aus dem Schlüsselwort void besteht, wird zur Deklaration von Funktionen verwendet, die keine Parameter entgegennehmen.
int f(void); // OK int g(void x); // Error
- jeder Bezeichner, der in einer Parameterliste vorkommt, die als typedef-Name oder als Parametername behandelt werden kann, wird als typedef-Name behandelt: int f(size_t, uintptr_t) wird als Deklarator im neuen Stil für eine Funktion geparst, die zwei unbenannte Parameter vom Typ size_t und uintptr_t entgegennimmt, nicht als Deklarator im alten Stil, der die Definition einer Funktion beginnt, die zwei Parameter mit den Namen "size_t" und "uintptr_t" entgegennimmt
- Parameter dürfen einen unvollständigen Typ haben und können die VLA-Notation [*] verwenden(seit C99) (außer dass in einer Funktionsdefinition die Parametertypen nach Array-zu-Zeiger- und Funktions-zu-Zeiger-Anpassung vollständig sein müssen)
|
Attribut-Spezifizierer-Sequenzen können auch auf Funktionsparameter angewendet werden. |
(seit C23) |
Siehe Funktionsaufrufoperator für weitere Details zu den Mechanismen eines Funktionsaufrufs und return für die Rückgabe aus Funktionen.
[bearbeiten] Anmerkungen
|
Im Gegensatz zu C++ haben die Deklaratoren f() und f(void) unterschiedliche Bedeutungen: Der Deklarator f(void) ist ein Deklarator im neuen Stil (mit Prototyp), der eine Funktion deklariert, die keine Parameter annimmt. Der Deklarator f() ist ein Deklarator, der eine Funktion deklariert, die eine *unbestimmte* Anzahl von Parametern annimmt (sofern nicht in einer Funktionsdefinition verwendet) int f(void); // declaration: takes no parameters int g(); // declaration: takes unknown parameters int main(void) { f(1); // compile-time error g(2); // undefined behavior } int f(void) { return 1; } // actual definition int g(a,b,c,d) int a,b,c,d; { return 2; } // actual definition |
(bis C23) |
Im Gegensatz zu einer Funktionsdefinition kann die Parameterliste von einem typedef geerbt werden
typedef int p(int q, int r); // p is a function type int(int, int) p f; // declares int f(int, int)
|
In C89 war specifiers-and-qualifiers optional, und wenn es weggelassen wurde, wurde der Rückgabetyp der Funktion standardmäßig als int angenommen (möglicherweise durch den declarator ergänzt). *f() { // function returning int* return NULL; } |
(bis C99) |
[bearbeiten] Fehlerberichte
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 wird implizit disqualifiziert |
[bearbeiten] Referenzen
- C17-Standard (ISO/IEC 9899:2018)
- 6.7.6.3 Funktionsdeklaratoren (einschließlich Prototypen) (S. 96-98)
- C11-Standard (ISO/IEC 9899:2011)
- 6.7.6.3 Funktionsdeklaratoren (einschließlich Prototypen) (S. 133-136)
- C99-Standard (ISO/IEC 9899:1999)
- 6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen) (S. 118-121)
- C89/C90-Standard (ISO/IEC 9899:1990)
- 3.5.4.3 Funktionsdeklaratoren (einschließlich Prototypen)
[bearbeiten] Siehe auch
| C++ Dokumentation für Funktionsdeklaration
|