Array-Deklaration
Ein Array ist ein Typ, der aus einer zusammenhängend zugewiesenen, nicht leeren Sequenz von Objekten mit einem bestimmten Elementtyp besteht. Die Anzahl dieser Objekte (die Array-Größe) ändert sich während der Lebensdauer des Arrays nie.
Inhalt |
[edit] Syntax
In der Grammatik für Deklarationen einer Array-Deklaration bezeichnet die Type-Specifier-Sequenz den Elementtyp (der ein vollständiger Objekttyp sein muss), und der Deklarator hat die Form
[ static(optional) qualifiers (optional) expression (optional) ] attr-spec-seq (optional) |
(1) | ||||||||
[ qualifiers (optional) static(optional) expression (optional) ] attr-spec-seq (optional) |
(2) | ||||||||
[ qualifiers (optional) * ] attr-spec-seq (optional) |
(3) | ||||||||
| expression | - | ein beliebiger Ausdruck außer dem Komma-Operator die Anzahl der Elemente im Array angibt |
| qualifiers | - | jede Kombination aus const, restrict oder volatile Qualifizierern, nur in Funktionsparameterlisten erlaubt; dies qualifiziert den Zeigertyp, in den dieser Array-Parameter umgewandelt wird |
| attr-spec-seq | - | (C23)optionale Liste von Attributen, angewendet auf das deklarierte Array |
float fa[11], *afp[17]; // fa is an array of 11 floats // afp is an array of 17 pointers to floats
[edit] Erklärung
Es gibt verschiedene Varianten von Array-Typen: Arrays mit bekannter konstanter Größe, Arrays mit variabler Länge und Arrays mit unbekannter Größe.
[edit] Arrays mit konstanter bekannter Größe
Wenn expression in einem Array-Deklarator ein Integer-Konstantausdruck mit einem Wert größer als Null ist und der Elementtyp ein Typ mit bekannter konstanter Größe ist (d.h. Elemente sind keine VLA)(seit C99), dann deklariert der Deklarator ein Array mit konstanter bekannter Größe.
int n[10]; // integer constants are constant expressions char o[sizeof(double)]; // sizeof is a constant expression enum { MAX_SZ=100 }; int n[MAX_SZ]; // enum constants are constant expressions
Arrays mit konstanter bekannter Größe können Array-Initialisierer verwenden, um ihre Anfangswerte bereitzustellen.
int a[5] = {1,2,3}; // declares int[5] initialized to 1,2,3,0,0 char str[] = "abc"; // declares char[4] initialized to 'a','b','c','\0'
|
In Funktionsparameterlisten sind zusätzliche Syntaxelemente innerhalb der Array-Deklaratoren erlaubt: das Schlüsselwort Bei jedem Aufruf einer Funktion, bei der ein Array-Parameter das Schlüsselwort void fadd(double a[static 10], const double b[static 10]) { for (int i = 0; i < 10; i++) { if (a[i] < 0.0) return; a[i] += b[i]; } } // a call to fadd may perform compile-time bounds checking // and also permits optimizations such as prefetching 10 doubles int main(void) { double a[10] = {0}, b[20] = {0}; fadd(a, b); // OK double x[5] = {0}; fadd(x, b); // undefined behavior: array argument is too small } Wenn qualifiers vorhanden sind, qualifizieren sie den Zeigertyp, in den der Array-Parameter-Typ umgewandelt wird. int f(const int a[20]) { // in this function, a has type const int* (pointer to const int) } int g(const int a[const 20]) { // in this function, a has type const int* const (const pointer to const int) } Dies wird üblicherweise mit dem Typ-Qualifizierer void fadd(double a[static restrict 10], const double b[static restrict 10]) { for (int i = 0; i < 10; i++) // loop can be unrolled and reordered { if (a[i] < 0.0) break; a[i] += b[i]; } } Arrays mit variabler Länge (Variable-length arrays, VLAs)Wenn expression kein Integer-Konstantausdruck ist, ist der Deklarator für ein Array mit variabler Größe. Jedes Mal, wenn der Kontrollfluss über die Deklaration läuft, wird expression ausgewertet (und muss immer zu einem Wert größer als Null ausgewertet werden), und das Array wird zugewiesen (entsprechend endet die Lebensdauer eines VLA, wenn die Deklaration außer Reichweite gerät). Die Größe jeder VLA-Instanz ändert sich während ihrer Lebensdauer nicht, aber bei einem erneuten Durchlauf durch denselben Code kann sie mit einer anderen Größe zugewiesen werden. Führen Sie diesen Code aus #include <stdio.h> int main(void) { int n = 1; label:; int a[n]; // re-allocated 10 times, each with a different size printf("The array has %zu elements\n", sizeof a / sizeof *a); if (n++ < 10) goto label; // leaving the scope of a VLA ends its lifetime } Wenn die Größe Variable-length arrays und die daraus abgeleiteten Typen (Zeiger darauf usw.) werden üblicherweise als "variably-modified types" (VM) bezeichnet. Objekte jedes variably-modified Typs dürfen nur im Block-Scope oder im Funktionsprototyp-Scope deklariert werden. extern int n; int A[n]; // Error: file scope VLA extern int (*p2)[n]; // Error: file scope VM int B[100]; // OK: file-scope array of constant known size void fvla(int m, int C[m][m]); // OK: prototype-scope VLA VLA müssen automatische oder zugewiesene Speicherklassendauer haben. Zeiger auf VLA, aber nicht VLA selbst, dürfen auch statische Speicherklassendauer haben. Kein VM-Typ darf eine Linkage haben. void fvla(int m, int C[m][m]) // OK: block scope/auto duration pointer to VLA { typedef int VLA[m][m]; // OK: block scope VLA int D[m]; // OK: block scope/auto duration VLA // static int E[m]; // Error: static duration VLA // extern int F[m]; // Error: VLA with linkage int (*s)[m]; // OK: block scope/auto duration VM s = malloc(m * sizeof(int)); // OK: s points to VLA in allocated storage // extern int (*r)[m]; // Error: VM with linkage static int (*q)[m] = &B; // OK: block scope/static duration VM} } Variably-modified types dürfen keine Mitglieder von structs oder unions sein. struct tag { int z[n]; // Error: VLA struct member int (*y)[n]; // Error: VM struct member }; |
(seit C99) |
|
Wenn der Compiler die Makrokonstante __STDC_NO_VLA__ auf den Integer-Konstantenwert 1 definiert, dann werden VLA und VM-Typen nicht unterstützt. |
(seit C11) (bis C23) |
|
Wenn der Compiler die Makrokonstante __STDC_NO_VLA__ auf den Integer-Konstantenwert 1 definiert, dann sind VLA-Objekte mit automatischer Speicherklassendauer nicht unterstützt. Die Unterstützung für VM-Typen und VLAs mit zugewiesener Speicherklassendauer ist vorgeschrieben. |
(seit C23) |
[edit] Arrays mit unbekannter Größe
Wenn expression in einem Array-Deklarator weggelassen wird, deklariert er ein Array unbekannter Größe. Außer in Funktionsparameterlisten (wo solche Arrays in Zeiger umgewandelt werden) und wenn ein Initialisierer verfügbar ist, ist ein solcher Typ ein unvollständiger Typ (beachten Sie, dass VLA unbekannter Größe, deklariert mit * als Größe, ein vollständiger Typ ist)(seit C99).
extern int x[]; // the type of x is "array of unknown bound of int" int a[] = {1,2,3}; // the type of a is "array of 3 int"
|
Innerhalb einer struct-Definition kann ein Array unbekannter Größe als letztes Mitglied erscheinen (vorausgesetzt, es gibt mindestens ein weiteres benanntes Mitglied), in diesem Fall ist es ein Sonderfall, der als flexibles Array-Mitglied bezeichnet wird. Weitere Details finden Sie unter struct. struct s { int n; double d[]; }; // s.d is a flexible array member struct s *s1 = malloc(sizeof (struct s) + (sizeof (double) * 8)); // as if d was double d[8]
|
(seit C99) |
[edit] Qualifizierer
|
Wenn ein Array-Typ mit einem |
(bis C23) |
|
Ein Array-Typ und sein Elementtyp gelten immer als identisch qualifiziert, mit der Ausnahme, dass ein Array-Typ niemals als |
(seit C23) |
typedef int A[2][3]; const A a = {{4, 5, 6}, {7, 8, 9}}; // array of array of const int int* pi = a[0]; // Error: a[0] has type const int* void* unqual_ptr = a; // OK until C23; error since C23 // Notes: clang applies the rule in C++/C23 even in C89-C17 modes
|
typedef int A[2]; // _Atomic A a0 = {0}; // Error // _Atomic(A) a1 = {0}; // Error _Atomic int a2[2] = {0}; // OK _Atomic(int) a3[2] = {0}; // OK |
(seit C11) |
[edit] Zuweisung
Objekte vom Array-Typ sind keine modifizierbaren L-Werte (modifiable lvalues), und obwohl ihre Adresse genommen werden kann, können sie nicht auf der linken Seite eines Zuweisungsoperators erscheinen. structs mit Array-Mitgliedern sind jedoch modifizierbare L-Werte und können zugewiesen werden.
int a[3] = {1,2,3}, b[3] = {4,5,6}; int (*p)[3] = &a; // okay, address of a can be taken // a = b; // error, a is an array struct { int c[3]; } s1, s2 = {3,4,5}; s1 = s2; // okay: can assign structs holding array members
[edit] Konvertierung von Arrays in Zeiger
Jeder L-Wert-Ausdruck (lvalue expression) vom Array-Typ wird, wenn er in einem anderen Kontext als
- als Operand des Adressoperator
- als Operand von
sizeof - als Operand von
typeofundtypeof_unqual(seit C23) - als String-Literal, das zur Array-Initialisierung verwendet wird
| (seit C11) |
einer impliziten Konvertierung in einen Zeiger auf sein erstes Element unterzogen. Das Ergebnis ist kein L-Wert.
Wenn das Array als register deklariert wurde, ist das Verhalten des Programms, das eine solche Konvertierung versucht, undefiniert.
Wenn ein Array-Typ in einer Funktionsparameterliste verwendet wird, wird er in den entsprechenden Zeigertyp umgewandelt: int f(int a[2]) und int f(int* a) deklarieren dieselbe Funktion. Da der tatsächliche Parameter-Typ der Funktion ein Zeigertyp ist, führt ein Funktionsaufruf mit einem Array-Argument eine Array-zu-Zeiger-Konvertierung durch; die Größe des Argument-Arrays ist für die aufgerufene Funktion nicht verfügbar und muss explizit übergeben werden.
#include <stdio.h> void f(int a[], int sz) // actually declares void f(int* a, int sz) { for (int i = 0; i < sz; ++i) printf("%d\n", a[i]); } void g(int (*a)[10]) // pointer to array parameter is not transformed { for (int i = 0; i < 10; ++i) printf("%d\n", (*a)[i]); } int main(void) { int a[10] = {0}; f(a, 10); // converts a to int*, passes the pointer g(&a); // passes a pointer to the array (no need to pass the size) }
[edit] Mehrdimensionale Arrays
Wenn der Elementtyp eines Arrays ein anderes Array ist, spricht man von einem mehrdimensionalen Array.
// array of 2 arrays of 3 ints each int a[2][3] = {{1,2,3}, // can be viewed as a 2x3 matrix {4,5,6}}; // with row-major layout
Beachten Sie, dass bei Anwendung der Array-zu-Zeiger-Konvertierung ein mehrdimensionales Array in einen Zeiger auf sein erstes Element konvertiert wird, z.B. in einen Zeiger auf die erste Zeile.
int a[2][3]; // 2x3 matrix int (*p1)[3] = a; // pointer to the first 3-element row int b[3][3][3]; // 3x3x3 cube int (*p2)[3][3] = b; // pointer to the first 3x3 plane
|
Mehrdimensionale Arrays können in jeder Dimension "variably modified" sein wenn VLAs unterstützt werden(seit C11). int n = 10; int a[n][2*n]; |
(seit C99) |
[edit] Anmerkungen
Deklarationen von Arrays der Länge Null sind nicht erlaubt, obwohl einige Compiler sie als Erweiterungen anbieten (typischerweise als vor-C99-Implementierung von flexiblen Array-Mitgliedern).
Wenn der Größen-Ausdruck expression eines VLA Seiteneffekte hat, ist garantiert, dass diese produziert werden, außer wenn er Teil eines sizeof-Ausdrucks ist, dessen Ergebnis nicht davon abhängt.
int n = 5, m = 5; size_t sz = sizeof(int (*[n++])[m++]); // n is incremented, m may or may not be incremented
[edit] Referenzen
- C23-Standard (ISO/IEC 9899:2024)
- 6.7.6.2 Array-Deklaratoren (S. TBD)
- C17-Standard (ISO/IEC 9899:2018)
- 6.7.6.2 Array-Deklaratoren (S. 94-96)
- C11-Standard (ISO/IEC 9899:2011)
- 6.7.6.2 Array-Deklaratoren (S. 130-132)
- C99-Standard (ISO/IEC 9899:1999)
- 6.7.5.2 Array-Deklaratoren (S. 116-118)
- C89/C90-Standard (ISO/IEC 9899:1990)
- 3.5.4.2 Array-Deklaratoren
[edit] Siehe auch
| C++-Dokumentation für Array-Deklaration
|