Deklarationen
Eine Deklaration ist ein Sprachkonstrukt in C, das einen oder mehrere Identifikatoren im Programm einführt und deren Bedeutung und Eigenschaften spezifiziert.
Deklarationen können in jedem Geltungsbereich auftreten. Jede Deklaration endet mit einem Semikolon (genau wie eine Anweisung) und besteht aus zwei(bis C23)drei(seit C23) verschiedenen Teilen
spezifizierer-und-qualifizierer deklaratoren-und-initialisierer (optional) ; |
(1) | ||||||||
attr-spec-seq spezifizierer-und-qualifizierer deklaratoren-und-initialisierer ; |
(2) | (seit C23) | |||||||
attr-spec-seq ; |
(3) | (seit C23) | |||||||
wobei
| spezifizierer-und-qualifizierer | - | durch Leerzeichen getrennte Liste von, in beliebiger Reihenfolge,
|
| deklaratoren-und-initialisierer | - | komma-getrennte Liste von Deklaratoren (jeder Deklarator liefert zusätzliche Typinformationen und/oder den zu deklarierenden Bezeichner). Deklaratoren können von Initialisierern begleitet werden. Die enum, struct und union Deklarationen können Deklaratoren weglassen, in welchem Fall sie nur die Aufzählungskonstanten und/oder Tags einführen. |
| attr-spec-seq | - | (C23)optionale Liste von Attributen, die auf die deklarierten Entitäten angewendet werden, oder bildet eine Attributdeklaration, wenn sie allein steht. |
Zum Beispiel,
int a, *b=NULL; // "int" is the type specifier, // "a" is a declarator // "*b" is a declarator and NULL is its initializer const int *f(void); // "int" is the type specifier // "const" is the type qualifier // "*f(void)" is the declarator enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier // "c" is the declarator
Der Typ jedes in einer Deklaration eingeführten Identifikators wird durch eine Kombination des durch den Typ-Spezifizierer angegebenen Typs und der durch seinen Deklarator angewendeten Typmodifikationen bestimmt. Der Typ einer Variablen kann auch abgeleitet werden, wenn der auto-Spezifizierer verwendet wird.(seit C23)
Attribute(seit C23) können in spezifizierer-und-qualifizierer auftreten, in welchem Fall sie auf den durch die vorhergehenden Spezifizierer bestimmten Typ angewendet werden.
Inhalt |
[bearbeiten] Deklaratoren
Jeder Deklarator ist einer der folgenden
| identifier attr-spec-seq (optional) | (1) | ||||||||
( deklarator ) |
(2) | ||||||||
* attr-spec-seq (optional) qualifizierer (optional) deklarator |
(3) | ||||||||
noptr-deklarator [ static(optional) qualifizierer (optional) ausdruck ]noptr-deklarator |
(4) | ||||||||
noptr-deklarator ( parameter-oder-identifikatoren ) |
(5) | ||||||||
D als einen cvr-qualifizierten Zeiger auf den von S bestimmten Typ.D als ein Array von N Objekten vom durch S bestimmten Typ. noptr-deklarator ist jeder andere Deklarator außer einem nicht in Klammern gesetzten Zeigerdeklarator.D als eine Funktion, die die Parameter params annimmt und S zurückgibt. noptr-deklarator ist jeder andere Deklarator außer einem nicht in Klammern gesetzten Zeigerdeklarator.Der Grund für diese Syntax ist, dass, wenn der durch den Deklarator deklarierte Identifikator in einem Ausdruck in derselben Form wie der Deklarator erscheint, er den vom Typ-Spezifizierer-Sequenz spezifizierten Typ hätte.
struct C { int member; // "int" is the type specifier // "member" is the declarator } obj, *pObj = &obj; // "struct C { int member; }" is the type specifier // declarator "obj" defines an object of type struct C // declarator "*pObj" declares a pointer to C, // initializer "= &obj" provides the initial value for that pointer int a = 1, *p = NULL, f(void), (*pf)(double); // the type specifier is "int" // declarator "a" defines an object of type int // initializer "=1" provides its initial value // declarator "*p" defines an object of type pointer to int // initializer "=NULL" provides its initial value // declarator "f(void)" declares a function taking void and returning int // declarator "(*pf)(double)" defines an object of type pointer // to function taking double and returning int int (*(*foo)(double))[3] = NULL; // the type specifier is int // 1. declarator "(*(*foo)(double))[3]" is an array declarator: // the type declared is "/nested declarator/ array of 3 int" // 2. the nested declarator is "*(*foo)(double))", which is a pointer declarator // the type declared is "/nested declarator/ pointer to array of 3 int" // 3. the nested declarator is "(*foo)(double)", which is a function declarator // the type declared is "/nested declarator/ function taking double and returning // pointer to array of 3 int" // 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by // function declarator syntax) pointer declarator. // the type declared is "/nested declarator/ pointer to function taking double // and returning pointer to array of 3 int" // 5. the nested declarator is "foo", which is an identifier. // The declaration introduces the identifier "foo" to refer to an object of type // "pointer to function taking double and returning pointer to array of 3 int" // The initializer "= NULL" provides the initial value of this pointer. // If "foo" is used in an expression of the form of the declarator, its type would be // int. int x = (*(*foo)(1.2))[0];
Das Ende jedes Deklarators, der nicht Teil eines anderen Deklarators ist, ist ein Sequenzpunkt.
In allen Fällen ist attr-spec-seq eine optionale Sequenz von Attributen(seit C23). Wenn sie unmittelbar nach dem Identifikator erscheint, gilt sie für das deklarierte Objekt oder die Funktion.
[bearbeiten] Definitionen
Eine Definition ist eine Deklaration, die alle Informationen über die von ihr deklarierten Identifikatoren bereitstellt.
Jede Deklaration einer enum oder eines typedef ist eine Definition.
Für Funktionen ist eine Deklaration, die den Funktionskörper enthält, eine Funktionsdefinition.
int foo(double); // declaration int foo(double x) { return x; } // definition
Für Objekte ist eine Deklaration, die Speicher allokiert (automatisch oder statisch, aber nicht extern), eine Definition, während eine Deklaration, die keinen Speicher allokiert (externe Deklaration), keine ist.
extern int n; // declaration int n = 10; // definition
Für structs und unions sind Deklarationen, die die Liste der Mitglieder angeben, Definitionen.
struct X; // declaration struct X { int n; }; // definition
[bearbeiten] Neudeklaration
Eine Deklaration kann keinen Identifikator einführen, wenn eine andere Deklaration für denselben Identifikator im selben Geltungsbereich früher erscheint, außer dass
- Deklarationen von Objekten mit Bindung (extern oder intern) wiederholt werden können.
extern int x; int x = 10; // OK extern int x; // OK static int n; static int n = 10; // OK static int n; // OK
- Nicht-VLA typedefs können wiederholt werden, solange sie denselben Typ benennen.
typedef int int_t; typedef int int_t; // OK
struct X; struct X { int n; }; struct X;
Diese Regeln vereinfachen die Verwendung von Header-Dateien.
[bearbeiten] Hinweise
|
In C89 müssen Deklarationen innerhalb jeder zusammengesetzten Anweisung (Block-Geltungsbereich) am Anfang des Blocks, vor allen Anweisungen stehen. Außerdem können in C89 Funktionen, die int zurückgeben, implizit durch den Funktionsaufrufoperator deklariert werden, und Funktionsparameter vom Typ int müssen nicht deklariert werden, wenn alte Funktionsdefinitionen verwendet werden. |
(bis C99) |
Leere Deklaratoren sind verboten; eine einfache Deklaration muss mindestens einen Deklarator haben oder mindestens einen struct/union/enum-Tag deklarieren oder mindestens eine Aufzählungskonstante einführen.
|
Wenn ein Teil eines Deklarators ein Variable-Length Array (VLA)-Deklarator ist, wird der gesamte Deklarator als "variabel modifizierter Typ" bezeichnet. Aus variabel modifizierten Typen definierte Typen sind ebenfalls variabel modifiziert (VM). Deklarationen von beliebigen variabel modifizierten Typen dürfen nur im Block-Geltungsbereich oder im Funktionsprototyp-Geltungsbereich auftreten und dürfen keine Mitglieder von structs oder unions sein. Obwohl VLA nur eine automatische oder allokierte Speicherdauer haben kann, kann ein VM-Typ wie ein Zeiger auf ein VLA statisch sein. Es gibt weitere Einschränkungen bei der Verwendung von VM-Typen, siehe goto, switch. longjmp |
(seit C99) |
|
static_asserts werden aus Sicht der C-Grammatik als Deklarationen betrachtet (so dass sie überall dort auftreten können, wo eine Deklaration auftreten kann), aber sie führen keine Identifikatoren ein und folgen nicht der Deklarationssyntax. |
(seit C11) |
|
Attributdeklarationen werden ebenfalls als Deklarationen betrachtet (so dass sie überall dort auftreten können, wo eine Deklaration auftreten kann), aber sie führen keine Identifikatoren ein. Ein einzelnes |
(seit C23) |
[bearbeiten] Referenzen
- C23-Standard (ISO/IEC 9899:2024)
- 6.7 Deklarationen (S. TBD)
- C17-Standard (ISO/IEC 9899:2018)
- 6.7 Deklarationen (S. 78-105)
- C11-Standard (ISO/IEC 9899:2011)
- 6.7 Deklarationen (S. 108-145)
- C99-Standard (ISO/IEC 9899:1999)
- 6.7 Deklarationen (S. 97-130)
- C89/C90-Standard (ISO/IEC 9899:1990)
- 3.5 Deklarationen
[bearbeiten] Siehe auch
| C++ Dokumentation für Deklarationen
|