Declarations
Eine Deklaration ist ein Konstrukt der C-Sprache, das einen oder mehrere Bezeichner in das Programm einführt und deren Bedeutung und Eigenschaften festlegt.
Deklarationen können in jedem Gültigkeitsbereich erscheinen. Jede Deklaration endet mit einem Semikolon (genau wie eine Anweisung ) und besteht aus zwei (until C23) drei (since C23) verschiedenen Teilen:
specifiers-and-qualifiers
declarators-and-initializers
(optional)
;
|
(1) | ||||||||
attr-spec-seq
specifiers-and-qualifiers
declarators-and-initializers
;
|
(2) | (seit C23) | |||||||
attr-spec-seq
;
|
(3) | (seit C23) | |||||||
wo
| specifiers-and-qualifiers | - |
durch Leerzeichen getrennte Liste, in beliebiger Reihenfolge,
|
| declarators-and-initializers | - | durch Kommas 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 diesem Fall führen sie nur die Enumerationskonstanten und/oder Tags ein. |
| attr-spec-seq | - | (C23) optionale Liste von Attributen , die auf die deklarierten Entitäten angewendet werden, oder bildet eine Attributdeklaration, wenn sie allein erscheint. |
Zum Beispiel,
int a, *b=NULL; // "int" ist der Typ-Spezifizierer, // "a" ist ein Deklarator // "*b" ist ein Deklarator und NULL ist sein Initialisierer const int *f(void); // "int" ist der Typ-Spezifizierer // "const" ist der Typ-Qualifizierer // "*f(void)" ist der Deklarator enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" ist der Typ-Spezifizierer // "c" ist der Deklarator
Der Typ jedes in einer Deklaration eingeführten Identifikators wird durch eine Kombination des durch den Typspezifizierer angegebenen Typs und der durch seinen Deklarator angewandten Typmodifikationen bestimmt. Der Typ einer Variable könnte auch abgeleitet werden, wenn der auto Spezifizierer verwendet wird. (seit C23)
Attribute (seit C23) können in specifiers-and-qualifiers erscheinen, in welchem Fall sie auf den durch die vorhergehenden Spezifizierer bestimmten Typ angewendet werden.
Inhaltsverzeichnis |
Deklaratoren
Jeder Deklarator ist einer der folgenden:
| identifier attr-spec-seq (optional) | (1) | ||||||||
(
declarator
)
|
(2) | ||||||||
*
attr-spec-seq
(optional)
qualifiers
(optional)
declarator
|
(3) | ||||||||
noptr-declarator
[
static
(optional)
qualifiers
(optional)
expression
]
noptr-declarator
|
(4) | ||||||||
noptr-declarator
(
parameters-or-identifiers
)
|
(5) | ||||||||
D
als einen
cvr
-qualifizierten Zeiger auf den durch
S
bestimmten Typ.
D
als ein Array von
N
Objekten des durch
S
bestimmten Typs.
noptr-declarator
ist jeder andere Deklarator außer einem nicht in Klammern gesetzten Pointer-Deklarator.
D
als eine Funktion, die die Parameter
params
entgegennimmt und
S
zurückgibt.
noptr-declarator
ist jeder andere Deklarator außer einem nicht in Klammern gesetzten Pointerdeklarator.
Die Begründung für diese Syntax ist, dass wenn der durch den Deklarator deklarierte Bezeichner in einem Ausdruck derselben Form wie der Deklarator erscheint, er den durch die Typspezifizierer-Sequenz angegebenen Typ haben würde.
struct C { int member; // "int" ist der Typ-Spezifizierer // "member" ist der Deklarator } obj, *pObj = &obj; // "struct C { int member; }" ist der Typ-Spezifizierer // Deklarator "obj" definiert ein Objekt vom Typ struct C // Deklarator "*pObj" deklariert einen Zeiger auf C, // Initialisierer "= &obj" liefert den Anfangswert für diesen Zeiger int a = 1, *p = NULL, f(void), (*pf)(double); // der Typ-Spezifizierer ist "int" // Deklarator "a" definiert ein Objekt vom Typ int // Initialisierer "=1" liefert seinen Anfangswert // Deklarator "*p" definiert ein Objekt vom Typ Zeiger auf int // Initialisierer "=NULL" liefert seinen Anfangswert // Deklarator "f(void)" deklariert eine Funktion, die void nimmt und int zurückgibt // Deklarator "(*pf)(double)" definiert ein Objekt vom Typ Zeiger // auf Funktion, die double nimmt und int zurückgibt int (*(*foo)(double))[3] = NULL; // der Typ-Spezifizierer ist int // 1. Deklarator "(*(*foo)(double))[3]" ist ein Array-Deklarator: // der deklarierte Typ ist "/verschachtelter Deklarator/ Array von 3 int" // 2. der verschachtelte Deklarator ist "*(*foo)(double))", was ein Zeiger-Deklarator ist // der deklarierte Typ ist "/verschachtelter Deklarator/ Zeiger auf Array von 3 int" // 3. der verschachtelte Deklarator ist "(*foo)(double)", was ein Funktions-Deklarator ist // der deklarierte Typ ist "/verschachtelter Deklarator/ Funktion, die double nimmt und // Zeiger auf Array von 3 int zurückgibt" // 4. der verschachtelte Deklarator ist "(*foo)", was ein (geklammerter, wie von der // Funktions-Deklarator-Syntax erfordert) Zeiger-Deklarator ist. // der deklarierte Typ ist "/verschachtelter Deklarator/ Zeiger auf Funktion, die double // nimmt und Zeiger auf Array von 3 int zurückgibt" // 5. der verschachtelte Deklarator ist "foo", was ein Bezeichner ist. // Die Deklaration führt den Bezeichner "foo" ein, um auf ein Objekt vom Typ // "Zeiger auf Funktion, die double nimmt und Zeiger auf Array von 3 int zurückgibt" zu verweisen // Der Initialisierer "= NULL" liefert den Anfangswert dieses Zeigers. // Wenn "foo" in einem Ausdruck der Form des Deklarators verwendet wird, wäre sein Typ // 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 Bezeichner erscheint, gilt sie für das deklarierte Objekt oder die deklarierte Funktion.
Definitionen
Eine Definition ist eine Deklaration, die alle Informationen über die von ihr deklarierten Bezeichner bereitstellt.
Jede Deklaration eines enum oder einer typedef ist eine Definition.
Für Funktionen ist eine Deklaration, die den Funktionskörper enthält, eine Funktionsdefinition :
int foo(double); // Deklaration 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 Definition ist.
extern int n; // Deklaration int n = 10; // Definition
Für structs und unions sind Deklarationen, die die Liste der Mitglieder angeben, Definitionen:
struct X; // Deklaration struct X { int n; }; // Definition
Neudeklaration
Eine Deklaration kann keinen Bezeichner einführen, falls eine andere Deklaration für denselben Bezeichner im selben Scope früher erscheint, außer dass
- Deklarationen von Objekten mit Linkage (external oder internal) können wiederholt werden:
extern int x; int x = 10; // OK extern int x; // OK static int n; static int n = 10; // OK static int n; // OK
- Non-VLA typedef kann wiederholt werden, solange es denselben Typ bezeichnet:
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.
Hinweise
|
In C89 müssen Deklarationen innerhalb jedes Verbundanweisungsblocks (Blockgültigkeitsbereich) am Anfang des Blocks erscheinen, vor allen Anweisungen . Außerdem können in C89 Funktionen, die int zurückgeben, implizit durch den Funktionsaufrufoperator deklariert werden, und Funktionsparameter vom Typ int müssen bei Verwendung alter Funktionsdefinitionen nicht deklariert werden. |
(bis C99) |
Leere Deklaratoren sind verboten; eine einfache Deklaration muss mindestens einen Deklarator haben oder mindestens ein Struct/Union/Enum-Tag deklarieren oder mindestens eine Enumerationskonstante einführen.
|
Wenn irgendein Teil eines Deklarators ein Array variabler Länge (VLA) ist, wird der Typ des gesamten Deklarators als "variabel modifizierter Typ" bezeichnet. Von variabel modifizierten Typen abgeleitete Typen sind ebenfalls variabel modifiziert (VM). Deklarationen von variabel modifizierten Typen dürfen nur im Blockgültigkeitsbereich oder Funktionsprototyp-Gültigkeitsbereich erscheinen und können keine Mitglieder von Strukturen oder Unions sein. Obwohl VLAs nur automatische oder allozierte Speicherdauer haben können, 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 (sodass sie überall erscheinen können, wo eine Deklaration erscheinen darf), aber sie führen keine Bezeichner ein und folgen nicht der Deklarationssyntax. |
(seit C11) |
|
Attribute
Deklarationen gelten ebenfalls als Deklarationen (sodass sie überall erscheinen können, wo eine Deklaration erscheinen darf), aber sie führen keine Bezeichner ein. Ein einzelnes
|
(seit C23) |
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
Siehe auch
|
C++ Dokumentation
für
Deklarationen
|