Namespaces
Variants

Declarations

From cppreference.net

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,
  • Typspezifizierer:


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.
1,2) Einfache Deklaration. Führt einen oder mehrere Bezeichner ein, die Objekte, Funktionen, Struct/Union/Enum-Tags, Typdefinitionen oder Aufzählungskonstanten bezeichnen.
3) Attributdeklaration. Deklariert keine Identifikatoren und hat implementierungsdefinierte Bedeutung, falls die Bedeutung nicht durch den Standard spezifiziert ist.

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 [ qualifiers  (optional) * ]

(4)
noptr-declarator ( parameters-or-identifiers ) (5)
1) der Bezeichner, den dieser Deklarator einführt.
2) Jeder Deklarator kann in Klammern eingeschlossen werden; dies ist erforderlich, um Zeiger auf Arrays und Zeiger auf Funktionen einzuführen.
3) Pointer-Deklarator : die Deklaration S * cvr D ; deklariert D als einen cvr -qualifizierten Zeiger auf den durch S bestimmten Typ.
4) Array-Deklarator : Die Deklaration S D [ N ] deklariert 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.
5) Funktionsdeklarator : die Deklaration S D ( params ) deklariert 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 ; ohne attr-spec-seq ist keine Attributdeklaration, sondern eine Anweisung.

(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