Array declaration
Deklariert ein Objekt vom Array-Typ.
Inhaltsverzeichnis |
Syntax
Eine Array-Deklaration ist jede einfache Deklaration, deren Deklarator die Form hat
noptr-declarator
[
expr
(optional)
]
attr
(optional)
|
|||||||||
| noptr-declarator | - |
jeder gültige
declarator
, aber wenn er mit
*
,
&
oder
&&
beginnt, muss er in Klammern gesetzt werden (andernfalls wird der gesamte Deklarator als
pointer declarator
oder
reference declarator
behandelt).
|
| expr | - | ein integral constant expression (bis C++14) ein converted constant expression vom Typ std::size_t (seit C++14) , der zu einem Wert größer als Null ausgewertet wird |
| attr | - | (seit C++11) Liste von attributes |
Eine Deklaration der Form
T a
[
N
]
;
deklariert
a
als ein Array-
Objekt
, das aus
N
zusammenhängend allozierten Objekten des Typs
T
besteht. Die Elemente eines Arrays werden von
0
bis
N
-
1
nummeriert und können mit dem
Subskript-Operator []
zugegriffen werden, wie in
a
[
0
]
bis
a
[
N
-
1
]
.
Arrays können aus jedem fundamentalen Typ (außer void ), Pointern , Pointer zu Membern , Klassen , Enumerationen oder aus anderen Arrays mit bekannter Grenze konstruiert werden (in diesem Fall wird das Array als mehrdimensional bezeichnet). Mit anderen Worten, nur Objekttypen mit Ausnahme von Arraytypen unbekannter Grenze können Elementtypen von Arraytypen sein. Arraytypen mit unvollständigem Elementtyp sind ebenfalls unvollständige Typen.
|
Der
möglicherweise
constrained
(since C++20)
|
(since C++11) |
Es gibt keine Arrays von Referenzen oder Arrays von Funktionen.
Das Anwenden von cv-qualifiers auf einen Array-Typ (durch typedef oder Template-Typ-Manipulation) wendet die Qualifizierer auf den Elementtyp an, aber jeder Array-Typ, dessen Elemente vom cv-qualifizierten Typ sind, wird als mit derselben cv-Qualifikation versehen betrachtet.
// a und b haben denselben const-qualifizierten Typ "Array von 5 const char" typedef const char CC; CC a[5] = {}; typedef char CA[5]; const CA b = {};
Bei Verwendung mit new[]-expression kann die Größe eines Arrays null sein; ein solches Array hat keine Elemente:
int* p = new int[0]; // Der Zugriff auf p[0] oder *p ist undefiniert delete[] p; // Bereinigung ist dennoch erforderlich
Zuweisung
Objekte vom Array-Typ können nicht als Ganzes modifiziert werden: Obwohl sie Lvalues sind (z.B. kann die Adresse eines Arrays genommen werden), können sie nicht auf der linken Seite eines Zuweisungsoperators stehen:
int a[3] = {1, 2, 3}, b[3] = {4, 5, 6}; int (*p)[3] = &a; // okay: Adresse von a kann genommen werden a = b; // Fehler: a ist ein Array struct { int c[3]; } s1, s2 = {3, 4, 5}; s1 = s2; // okay: implizit definierter Kopierzuweisungsoperator // kann Datenelemente vom Array-Typ zuweisen
Array-zu-Pointer-Zerfall
Es gibt eine implizite Konvertierung von Lvalues und Rvalues des Array-Typs zu Rvalues des Pointer-Typs: Sie konstruiert einen Pointer zum ersten Element eines Arrays. Diese Konvertierung wird verwendet, wann immer Arrays in Kontexten erscheinen, in denen Arrays nicht erwartet werden, aber Pointer erwartet werden:
#include <iostream> #include <iterator> #include <numeric> void g(int (&a)[3]) { std::cout << a[0] << '\n'; } void f(int* p) { std::cout << *p << '\n'; } int main() { int a[3] = {1, 2, 3}; int* p = a; std::cout << sizeof a << '\n' // gibt die Größe des Arrays aus << sizeof p << '\n'; // gibt die Größe eines Zeigers aus // wo Arrays akzeptiert werden, aber Zeiger nicht, dürfen nur Arrays verwendet werden g(a); // okay: Funktion nimmt ein Array per Referenz // g(p); // Fehler for (int n : a) // okay: Arrays können in Range-for-Schleifen verwendet werden std::cout << n << ' '; // gibt Elemente des Arrays aus // for (int n : p) // Fehler // std::cout << n << ' '; std::iota(std::begin(a), std::end(a), 7); // okay: begin und end nehmen Arrays // std::iota(std::begin(p), std::end(p), 7); // Fehler // wo Zeiger akzeptiert werden, aber Arrays nicht, können beide verwendet werden: f(a); // okay: Funktion nimmt einen Zeiger f(p); // okay: Funktion nimmt einen Zeiger std::cout << *a << '\n' // gibt das erste Element aus << *p << '\n' // dasselbe << *(a + 1) << ' ' << a[1] << '\n' // gibt das zweite Element aus << *(p + 1) << ' ' << p[1] << '\n'; // dasselbe }
Mehrdimensionale Arrays
Wenn der Elementtyp eines Arrays ein anderes Array ist, wird gesagt, dass das Array mehrdimensional ist:
// Array von 2 Arrays mit jeweils 3 int int a[2][3] = {{1, 2, 3}, // kann als 2×3-Matrix betrachtet werden {4, 5, 6}}; // mit row-major layout
Beachten Sie, dass bei Array-zu-Pointer-Zerfall ein mehrdimensionales Array in einen Zeiger auf sein erstes Element umgewandelt wird (z.B. ein Zeiger auf seine erste Zeile oder seine erste Ebene): Array-zu-Pointer-Zerfall wird nur einmal angewendet.
int a[2]; // Array von 2 int int* p1 = a; // a zerfällt zu einem Zeiger auf das erste Element von a int b[2][3]; // Array von 2 Arrays von 3 int // int** p2 = b; // Fehler: b zerfällt nicht zu int** int (*p2)[3] = b; // b zerfällt zu einem Zeiger auf die erste 3-Element-Zeile von b int c[2][3][4]; // Array von 2 Arrays von 3 Arrays von 4 int // int*** p3 = c; // Fehler: c zerfällt nicht zu int*** int (*p3)[3][4] = c; // c zerfällt zu einem Zeiger auf die erste 3 × 4-Element-Ebene von c
Arrays unbekannter Größe
Wenn expr in der Deklaration eines Arrays weggelassen wird, ist der deklarierte Typ "Array unbekannter Größe von T", was eine Art unvollständiger Typ ist, außer wenn es in einer Deklaration mit einem Aggregat-Initialisierer verwendet wird:
extern int x[]; // der Typ von x ist "Array unbekannter Größe von int" int a[] = {1, 2, 3}; // der Typ von a ist "Array von 3 int"
Da Array-Elemente keine Arrays unbekannter Größe sein können, können mehrdimensionale Arrays keine unbekannte Größe in einer anderen Dimension als der ersten haben:
extern int a[][2]; // okay: Array mit unbekannter Größe von Arrays mit 2 int extern int b[2][]; // Fehler: Array hat unvollständigen Elementtyp
Falls eine vorherige Deklaration der Entität im selben Gültigkeitsbereich existiert, in dem die Grenze angegeben wurde, wird eine ausgelassene Array-Grenze als dieselbe wie in dieser früheren Deklaration angenommen, und ähnlich für die Definition eines statischen Datenelements einer Klasse:
extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: Grenze ist 10 int S::y[]; // OK: Grenze ist 10 void f() { extern int x[]; int i = sizeof(x); // Fehler: unvollständiger Objekttyp }
Referenzen und Zeiger auf Arrays unbekannter Größe können gebildet werden, können aber nicht (bis C++20) und können (seit C++20) von Arrays und Zeigern auf Arrays bekannter Größe initialisiert oder zugewiesen werden. Beachten Sie, dass in der Programmiersprache C Zeiger auf Arrays unbekannter Größe mit Zeigern auf Arrays bekannter Größe kompatibel sind und daher in beide Richtungen konvertierbar und zuweisbar sind.
extern int a1[]; int (&r1)[] = a1; // in Ordnung int (*p1)[] = &a1; // in Ordnung int (*q)[2] = &a1; // Fehler (aber in C in Ordnung) int a2[] = {1, 2, 3}; int (&r2)[] = a2; // in Ordnung (seit C++20) int (*p2)[] = &a2; // in Ordnung (seit C++20)
Zeiger auf Arrays unbekannter Größe können nicht an Zeigerarithmetik teilnehmen und können nicht auf der linken Seite des Subskriptoperators verwendet werden, können aber dereferenziert werden.
Array-Rvalues
Obwohl Arrays nicht als Wert von Funktionen zurückgegeben werden können und nicht Ziel der meisten Cast-Ausdrücke sein können, können Array- prvalues gebildet werden, indem ein Typalias verwendet wird, um ein Array-Temporary mittels funktionaler Cast mit Initialisiererliste zu konstruieren.
|
Wie Klassen-PRWerte konvertieren Array-PRWerte durch temporäre Materialisierung zu XWerten, wenn sie ausgewertet werden. |
(since C++17) |
Array xvalues können direkt durch Zugriff auf ein Array-Mitglied eines Klassen-rvalues gebildet werden oder durch Verwendung von std::move oder einer anderen Umwandlung oder Funktionsaufruf, der eine Rvalue-Referenz zurückgibt.
#include <iostream> #include <type_traits> #include <utility> void f(int (&&x)[2][3]) { std::cout << sizeof x << '\n'; } struct X { int i[2][3]; } x; template<typename T> using identity = T; int main() { std::cout << sizeof X().i << '\n'; // Größe des Arrays f(X().i); // okay: bindet an xvalue // f(x.i); // Fehler: kann nicht an lvalue binden int a[2][3]; f(std::move(a)); // okay: bindet an xvalue using arr_t = int[2][3]; f(arr_t{}); // okay: bindet an prvalue f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // okay: bindet an prvalue }
Ausgabe:
24 24 24 24 24
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 393 | C++98 |
Ein Zeiger oder eine Referenz auf ein Array unbekannter
Größe konnte kein Funktionsparameter sein |
erlaubt |
| CWG 619 | C++98 |
Wenn ausgelassen, konnte die Größe eines Arrays nicht
von einer vorherigen Deklaration abgeleitet werden |
Ableitung erlaubt |
| CWG 2099 | C++98 |
Die Größe eines Array-Static-Datenmembers konnte
nicht ausgelassen werden, selbst wenn ein Initialisierer vorhanden war |
Auslassung erlaubt |
| CWG 2397 | C++11 | auto konnte nicht als Elementtyp verwendet werden | erlaubt |
Siehe auch
|
C-Dokumentation
für
Array-Deklaration
|