Namespaces
Variants

Type

From cppreference.net

(Siehe auch arithmetic types für Details zu den meisten eingebauten Typen und die Liste der typbezogenen Hilfsmittel die von der C-Bibliothek bereitgestellt werden.)

Objekte , Funktionen und Ausdrücke besitzen eine Eigenschaft namens Typ , die die Interpretation des binären Werts bestimmt, der in einem Objekt gespeichert oder durch einen Ausdruck ausgewertet wird.

Inhaltsverzeichnis

Typklassifizierung

Das C-Typsystem besteht aus den folgenden Typen:

  • der Typ void
  • Grundtypen
  • der Typ char
  • vorzeichenbehaftete Ganzzahltypen
  • Standard: signed char , short , int , long , long long (seit C99)
  • bitgenau: _BitInt ( N ) wobei N ein ganzzahliger konstanter Ausdruck ist, der die Anzahl der Bits angibt, die zur Darstellung des Typs verwendet werden, einschließlich des Vorzeichenbits. Jeder Wert von N bezeichnet einen distinct Typ.
(seit C23)
  • erweitert: implementierungsdefiniert, z.B. __int128
(seit C99)
  • vorzeichenlose Ganzzahltypen
  • Standard: _Bool , (since C99) unsigned char , unsigned short , unsigned int , unsigned long , unsigned long long (since C99)
  • Bitgenau: unsigned _BitInt ( N ) wobei N ein ganzzahliger konstanter Ausdruck ist, der die Anzahl der Bits zur Darstellung des Typs angibt. Jeder Wert von N bezeichnet einen distincten Typ. Diese Kategorie enthält den Typ unsigned _BitInt ( 1 ) , der keinen entsprechenden bitgenauen vorzeichenbehafteten Ganzzahltyp besitzt.
(seit C23)
  • Erweitert: implementierungsdefiniert, z.B. __uint128
(seit C99)
  • Gleitkommatypen
  • Reelle Gleitkommatypen: float , double , long double
  • Dezimale Gleitkommatypen: _Decimal32 , _Decimal64 , _Decimal128
(seit C23)
  • Komplexe Typen: float _Complex , double _Complex , long double _Complex
  • Imaginäre Typen: float _Imaginary , double _Imaginary , long double _Imaginary
(seit C99)
  • abgeleitete Typen
(seit C11)

Für jeden oben aufgeführten Typ können mehrere qualifizierte Versionen seines Typs existieren, entsprechend den Kombinationen von einem, zwei oder allen drei der const -, volatile - und restrict -Qualifizierer (sofern durch die Semantik des Qualifizierers erlaubt).

Typgruppen

  • Objekttypen : alle Typen, die keine Funktionstypen sind
  • Zeichentypen : char , signed char , unsigned char
  • Ganzzahltypen : char , vorzeichenbehaftete Ganzzahltypen, vorzeichenlose Ganzzahltypen, Aufzählungstypen
  • reale Typen : Ganzzahltypen und Gleitkommatypen
  • arithmetische Typen : Ganzzahltypen und Gleitkommatypen
  • skalare Typen : arithmetische Typen, Zeigertypen , und nullptr_t (seit C23)
  • zusammengesetzte Typen : Arraytypen und Strukturtypen
  • abgeleitete Deklaratortypen : Arraytypen, Funktionstypen und Zeigertypen

Das Konstruieren eines vollständigen Objekttyps, sodass die Anzahl der Bytes in seiner Objektrepräsentation nicht im Typ size_t darstellbar ist (d.h. dem Ergebnistyp des sizeof Operators) , einschließlich der Bildung eines solchen VLA-Typs zur Laufzeit, (seit C99) ist undefiniertes Verhalten.

Kompatible Typen

In einem C-Programm müssen die Deklarationen, die sich auf dasselbe Objekt oder dieselbe Funktion in verschiedenen Übersetzungseinheiten beziehen, nicht denselben Typ verwenden. Sie müssen nur ausreichend ähnliche Typen verwenden, formal bekannt als kompatible Typen . Dasselbe gilt für Funktionsaufrufe und Lvalue-Zugriffe; Argumenttypen müssen kompatibel mit Parametertypen sein und der Lvalue-Ausdrucktyp muss kompatibel mit dem Objekttyp sein, auf den zugegriffen wird.

Die Typen T und U sind kompatibel, wenn

  • sie sind vom gleichen Typ (gleicher Name oder durch eine typedef eingeführte Aliase)
  • sie sind identisch cvr-qualifizierte Versionen kompatibler unqualifizierter Typen
  • sie sind Zeigertypen und zeigen auf kompatible Typen
  • sie sind Arraytypen, und
  • ihre Elementtypen kompatibel sind, und
  • wenn beide eine konstante Größe haben, diese Größe gleich ist. Hinweis: Arrays unbekannter Grenze sind mit jedem Array kompatiblen Elementtyps kompatibel. VLA ist mit jedem Array kompatiblen Elementtyps kompatibel. (seit C99)
  • sie sind beide Struktur-/Union-/Aufzählungstypen, und
  • (C99) falls einer mit einem Tag deklariert wird, muss der andere ebenfalls mit demselben Tag deklariert werden.
  • wenn beide vollständige Typen sind, müssen ihre Mitglieder genau in der Anzahl übereinstimmen, mit kompatiblen Typen deklariert sein und übereinstimmende Namen haben.
  • zusätzlich, wenn es sich um Enumerationen handelt, müssen entsprechende Mitglieder auch dieselben Werte haben.
  • zusätzlich, wenn es sich um Strukturen oder Unions handelt,
  • Entsprechende Mitglieder müssen in derselben Reihenfolge deklariert werden (nur bei Strukturen)
  • Entsprechende Bitfelder müssen dieselben Breiten haben.
  • einer ist ein Aufzählungstyp und der andere ist der zugrundeliegende Typ dieser Aufzählung
  • es sich um Funktionstypen handelt, und
  • ihre Rückgabetypen sind kompatibel
  • beide verwenden Parameterlisten, die Anzahl der Parameter (einschließlich der Verwendung der Ellipse) ist gleich, und die entsprechenden Parameter haben nach Anwendung von Array-zu-Zeiger- und Funktion-zu-Zeiger-Typanpassungen und nach Entfernen von Top-Level-Qualifizierern kompatible Typen
  • eine ist eine alte (parameterlose) Definition, die andere hat eine Parameterliste, die Parameterliste verwendet keine Ellipse und jeder Parameter ist (nach Funktionsparametertypanpassung) kompatibel mit dem entsprechenden alten Parameter nach Standardargumentpromotion
  • eine ist eine alte (parameterlose) Deklaration, die andere hat eine Parameterliste, die Parameterliste verwendet keine Ellipse, und alle Parameter (nach Funktionsparametertypanpassung) sind von Standardargumentpromotion nicht betroffen
(bis C23)

Der Typ char ist nicht kompatibel mit signed char und nicht kompatibel mit unsigned char .

Wenn zwei Deklarationen auf dasselbe Objekt oder dieselbe Funktion verweisen und keine kompatiblen Typen verwenden, ist das Verhalten des Programms undefiniert.

// Übersetzungseinheit 1
struct S { int a; };
extern struct S *x; // kompatibel mit TU2's x, aber nicht mit TU3's x
// Übersetzungseinheit 2
struct S;
extern struct S *x; // kompatibel mit beiden x's
// Übersetzungseinheit 3
struct S { float a; };
extern struct S *x; // kompatibel mit TU2's x, aber nicht mit TU1's x
// das Verhalten ist undefiniert
// Übersetzungseinheit 1
#include <stdio.h>
struct s { int i; }; // kompatibel mit TU3's s, aber nicht mit TU2's
extern struct s x = {0}; // kompatibel mit TU3's x
extern void f(void); // kompatibel mit TU2's f
int main()
{
    f();
    return x.i;
}
// Übersetzungseinheit 2
struct s { float f; }; // kompatibel mit TU4's s, aber nicht mit TU1's s
extern struct s y = {3.14}; // kompatibel mit TU4's y
void f() // kompatibel mit TU1's f
{
    return;
}
// Übersetzungseinheit 3
struct s { int i; }; // kompatibel mit TU1's s, aber nicht mit TU2's s
extern struct s x; // kompatibel mit TU1's x
// Übersetzungseinheit 4
struct s { float f; }; // kompatibel mit TU2's s, aber nicht mit TU1's s
extern struct s y; // kompatibel mit TU2's y
// das Verhalten ist wohldefiniert: nur mehrere Deklarationen
// von Objekten und Funktionen müssen kompatible Typen haben, nicht die Typen selbst

Hinweis: C++ hat kein Konzept kompatibler Typen. Ein C-Programm, das zwei Typen deklariert, die kompatibel aber nicht identisch in verschiedenen Übersetzungseinheiten sind, ist kein gültiges C++-Programm.

Zusammengesetzte Typen

Ein zusammengesetzter Typ kann aus zwei kompatiblen Typen konstruiert werden; es handelt sich um einen Typ, der mit beiden der beiden Typen kompatibel ist und die folgenden Bedingungen erfüllt:

  • Wenn beide Typen Array-Typen sind, werden die folgenden Regeln angewendet:
  • Wenn ein Typ ein Array bekannter konstanter Größe ist, ist der zusammengesetzte Typ ein Array dieser Größe.
  • Andernfalls, wenn ein Typ ein VLA ist, dessen Größe durch einen nicht ausgewerteten Ausdruck spezifiziert ist, hat ein Programm, das den zusammengesetzten Typ beider Typen benötigt, undefiniertes Verhalten.
  • Andernfalls, wenn ein Typ ein VLA mit spezifizierter Größe ist, ist der zusammengesetzte Typ ein VLA dieser Größe.
  • Andernfalls, wenn ein Typ ein VLA nicht spezifizierter Größe ist, ist der zusammengesetzte Typ ein VLA nicht spezifizierter Größe.
(seit C99)
  • Andernfalls sind beide Typen Arrays unbekannter Größe und der zusammengesetzte Typ ist ein Array unbekannter Größe.
Der Elementtyp des zusammengesetzten Typs ist der zusammengesetzte Typ der beiden Elementtypen.
  • Wenn nur ein Typ ein Funktionstyp mit einer Parametertypliste (ein Funktionsprototyp) ist, dann ist der zusammengesetzte Typ ein Funktionsprototyp mit der Parametertypliste.
(bis C23)
  • Wenn beide Typen Funktionstypen mit Parametertyplisten sind, ist der Typ jedes Parameters in der zusammengesetzten Parametertypliste der zusammengesetzte Typ der entsprechenden Parameter.

Diese Regeln gelten rekursiv für die Typen, von denen die beiden Typen abgeleitet sind.

// Bei den folgenden zwei Deklarationen im Dateibereich:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]); // C23: Fehler: widersprüchliche Typen für 'f'
// Der resultierende zusammengesetzte Typ für die Funktion ist:
int f(int (*)(char *), double (*)[3]);

Für einen Bezeichner mit interner oder externer Verknüpfung , der in einem Gültigkeitsbereich deklariert wird, in dem eine vorherige Deklaration dieses Bezeichners sichtbar ist, wenn die vorherige Deklaration interne oder externe Verknüpfung spezifiziert, wird der Typ des Bezeichners bei der späteren Deklaration zum zusammengesetzten Typ.

Unvollständige Typen

Ein unvollständiger Typ ist ein Objekttyp, dem ausreichende Informationen fehlen, um die Größe der Objekte dieses Typs zu bestimmen. Ein unvollständiger Typ kann an einem bestimmten Punkt in der Übersetzungseinheit vervollständigt werden.

Die folgenden Typen sind unvollständig:

  • der Typ void . Dieser Typ kann nicht vervollständigt werden.
  • Array-Typ unbekannter Größe. Er kann durch eine spätere Deklaration vervollständigt werden, die die Größe angibt.
extern char a[]; // der Typ von a ist unvollständig (dies erscheint typischerweise in einem Header)
char a[10];      // der Typ von a ist nun vollständig (dies erscheint typischerweise in einer Quelldatei)
  • Struktur- oder Union-Typ unbekannten Inhalts. Er kann durch eine Deklaration derselben Struktur oder Union vervollständigt werden, die seinen Inhalt später im selben Gültigkeitsbereich definiert.
struct node
{
    struct node* next; // struct node ist an diesem Punkt unvollständig
}; // struct node ist an diesem Punkt vollständig

Typnamen

Ein Typ muss möglicherweise in einem anderen Kontext als der Deklaration benannt werden. In diesen Situationen wird ein Typname verwendet, der grammatikalisch genau dasselbe ist wie eine Liste von type-specifiers und type-qualifiers , gefolgt vom declarator (siehe declarations ), wie er verwendet würde, um ein einzelnes Objekt oder eine Funktion dieses Typs zu deklarieren, mit der Ausnahme, dass der Bezeichner weggelassen wird:

int n; // Deklaration eines int
sizeof(int); // Verwendung eines Typnamens
int *a[3]; // Deklaration eines Arrays von 3 Zeigern auf int
sizeof(int *[3]); // Verwendung eines Typnamens
int (*p)[3]; // Deklaration eines Zeigers auf ein Array von 3 int
sizeof(int (*)[3]); // Verwendung eines Typnamens
int (*a)[*] // Deklaration eines Zeigers auf VLA (in einem Funktionsparameter)
sizeof(int (*)[*]) // Verwendung eines Typnamens (in einem Funktionsparameter)
int *f(void); // Deklaration einer Funktion
sizeof(int *(void)); // Verwendung eines Typnamens
int (*p)(void); // Deklaration eines Zeigers auf eine Funktion
sizeof(int (*)(void)); // Verwendung eines Typnamens
int (*const a[])(unsigned int, ...) = {0}; // Array von Zeigern auf Funktionen
sizeof(int (*const [])(unsigned int, ...)); // Verwendung eines Typnamens

Außer die redundanten Klammern um den Bezeichner sind in einem Typnamen bedeutungsvoll und repräsentieren "Funktion ohne Parameterangabe":

int (n); // deklariert n vom Typ int
sizeof(int ()); // verwendet Typ "Funktion, die int zurückgibt"

Typnamen werden in den folgenden Situationen verwendet:

(seit C99)
(seit C11)


Ein Typname kann einen neuen Typ einführen:

void* p = (void*)(struct X { int i; } *)0;
// Der Typname "struct X {int i;}*" im Cast-Ausdruck
// führt den neuen Typ "struct X" ein
struct X x = {1}; // struct X ist jetzt im Gültigkeitsbereich

Referenzen

  • C23-Standard (ISO/IEC 9899:2024):
  • 6.2.5 Typen (S.: TBD)
  • 6.2.6 Darstellungen von Typen (S.: TBD)
  • 6.2.7 Kompatibler Typ und zusammengesetzter Typ (S.: TBD)
  • C17-Standard (ISO/IEC 9899:2018):
  • 6.2.5 Typen (S: 31-33)
  • 6.2.6 Darstellungen von Typen (S: 31-35)
  • 6.2.7 Kompatibler Typ und zusammengesetzter Typ (S: 35-36)
  • C11-Standard (ISO/IEC 9899:2011):
  • 6.2.5 Typen (S: 39-43)
  • 6.2.6 Darstellungen von Typen (S: 44-46)
  • 6.2.7 Kompatibler Typ und zusammengesetzter Typ (S: 47-48)
  • C99-Standard (ISO/IEC 9899:1999):
  • 6.2.5 Typen (S: 33-37)
  • 6.2.6 Darstellungen von Typen (S: 37-40)
  • 6.2.7 Kompatibler Typ und zusammengesetzter Typ (S: 40-41)
  • C89/C90 Standard (ISO/IEC 9899:1990):
  • 3.1.2.5 Typen
  • 3.1.2.6 Kompatible Typen und zusammengesetzte Typen

Siehe auch