Namensräume
Varianten
Aktionen

Array-Deklaration

Von cppreference.com
< c‎ | Sprache

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)
1,2) Allgemeine Syntax für Array-Deklaratoren
3) Deklarator für VLA unbekannter Größe (kann nur im Funktionsprototyp-Scope erscheinen), wobei
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 static und qualifiers, die in beliebiger Reihenfolge vor dem Größen-Ausdruck erscheinen können (sie können auch erscheinen, wenn der Größen-Ausdruck weggelassen wird).

Bei jedem Aufruf einer Funktion, bei der ein Array-Parameter das Schlüsselwort static zwischen [ und ] verwendet, muss der Wert des tatsächlichen Parameters ein gültiger Zeiger auf das erste Element eines Arrays mit mindestens so vielen Elementen sein, wie durch expression angegeben.

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 restrict verwendet.

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.

#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 * ist, ist die Deklaration für ein VLA unbekannter Größe. Eine solche Deklaration darf nur im Funktionsprototyp-Scope erscheinen und deklariert ein Array eines vollständigen Typs. Tatsächlich werden alle VLA-Deklaratoren im Funktionsprototyp-Scope behandelt, als ob expression durch * ersetzt worden wäre.

void foo(size_t x, int a[*]);
void foo(size_t x, int a[x])
{
    printf("%zu\n", sizeof a); // same as sizeof(int*)
}

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 const, volatile, oder restrict(seit C99) Qualifizierer deklariert wird (was durch die Verwendung von typedef möglich ist), ist der Array-Typ nicht qualifiziert, aber sein Elementtyp ist es.

(bis C23)

Ein Array-Typ und sein Elementtyp gelten immer als identisch qualifiziert, mit der Ausnahme, dass ein Array-Typ niemals als _Atomic-qualifiziert gilt.

(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

_Atomic darf nicht auf einen Array-Typ angewendet werden, obwohl ein Array vom atomaren Typ erlaubt ist.

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

(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.

int a[3] = {1,2,3};
int* p = a;
printf("%zu\n", sizeof a); // prints size of array
printf("%zu\n", sizeof p); // prints size of a pointer

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