Freigeben über


Arrays (C++)

Ein Array ist eine Abfolge von Objekten desselben Typs, die einen zusammenhängenden Speicherbereich belegen. Herkömmliche C-Stil-Arrays sind die Quelle vieler Fehler, sind aber immer noch üblich, insbesondere in älteren Codebasen. In modernem C++ wird dringend empfohlen, Arrays im C-Stil zu verwenden std::vector oder std::array zu verwenden, die in diesem Abschnitt beschrieben werden. Beide Standardbibliothekstypen speichern ihre Elemente als zusammenhängenden Speicherblock. Sie bieten jedoch eine größere Typsicherheit und unterstützen Iteratoren, die garantiert auf eine gültige Position innerhalb der Sequenz verweisen. Weitere Informationen finden Sie unter Container.

Stapeldeklarationen

In einer C++-Arraydeklaration wird die Arraygröße nach dem Variablennamen angegeben, nicht nach dem Typnamen wie in einigen anderen Sprachen. Im folgenden Beispiel wird ein Array von 1000 Doubles deklariert, das auf dem Stapel zugeordnet werden soll. Die Anzahl der Elemente muss als ganzzahliges Literal oder sonst als Konstantenausdruck angegeben werden. Das liegt daran, dass der Compiler wissen muss, wie viel Stapelspeicher zuzuweisen ist; es kann keinen zur Laufzeit berechneten Wert verwenden. Jedem Element im Array wird ein Standardwert von 0 zugewiesen. Wenn Sie keinen Standardwert zuweisen, enthält jedes Element zunächst die zufälligen Werte an diesem Speicherort.

    constexpr size_t size = 1000;

    // Declare an array of doubles to be allocated on the stack
    double numbers[size] {0};

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i-1] * 1.1;
    }

    // Access each element
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

Das erste Element im Array ist das Null-Element. Das letzte Element ist das (n-1)-Element, wobei n die Anzahl der Elemente ist, die das Array enthalten kann. Die Anzahl der Elemente in der Deklaration muss ein integraler Typ sein und muss größer als 0 sein. Es liegt in Ihrer Verantwortung, sicherzustellen, dass Ihr Programm niemals einen Wert an den tiefgestellten Operator übergibt, der größer als (size - 1)ist.

Ein Array mit Nullgröße ist nur zulässig, wenn das Array das letzte Feld in einem struct oder und union wenn die Microsoft-Erweiterungen aktiviert sind (/Za oder /permissive- nicht festgelegt sind).

Stapelbasierte Arrays sind schneller zuzuordnen und darauf zuzugreifen als heapbasierte Arrays. Stapelplatz ist jedoch begrenzt. Die Anzahl der Arrayelemente kann nicht so groß sein, dass sie zu viel Stapelspeicher belegt. Wie viel zu viel ist, hängt von Ihrem Programm ab. Mithilfe von Profilerstellungstools können Sie ermitteln, ob ein Array zu groß ist.

Heap-Deklarationen

Möglicherweise benötigen Sie ein Array, das zu groß ist, um den Stapel zuzuordnen, oder dessen Größe zur Kompilierungszeit nicht bekannt ist. Es ist möglich, dieses Array mithilfe eines new[] Ausdrucks auf dem Heap zuzuweisen. Der Operator gibt einen Zeiger auf das erste Element zurück. Der Tiefstellungsoperator funktioniert auf die Zeigervariable auf die gleiche Weise wie auf einem stapelbasierten Array. Sie können auch Zeigerarithmetik verwenden, um den Zeiger auf beliebige Elemente im Array zu verschieben. Es liegt in Ihrer Verantwortung, folgendes zu gewährleisten:

  • Sie behalten immer eine Kopie der ursprünglichen Zeigeradresse bei, damit Sie den Speicher löschen können, wenn Sie das Array nicht mehr benötigen.
  • Sie erhöhen oder verringern die Zeigeradresse nicht über die Arraygrenzen hinaus.

Das folgende Beispiel zeigt, wie Sie ein Array für den Heap zur Laufzeit definieren. Es zeigt, wie Sie mithilfe des Tiefstellungsoperators und mithilfe der Zeigerarithmetik auf die Arrayelemente zugreifen:

void do_something(size_t size)
{
    // Declare an array of doubles to be allocated on the heap
    double* numbers = new double[size]{ 0 };

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i - 1] * 1.1;
    }

    // Access each element with subscript operator
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

    // Access each element with pointer arithmetic
    // Use a copy of the pointer for iterating
    double* p = numbers;

    for (size_t i = 0; i < size; i++)
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    // Alternate method:
    // Reset p to numbers[0]:
    p = numbers;

    // Use address of pointer to compute bounds.
    // The compiler computes size as the number
    // of elements * (bytes per element).
    while (p < (numbers + size))
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    delete[] numbers; // don't forget to do this!

}
int main()
{
    do_something(108);
}

Initialisieren von Arrays

Sie können ein Array in einer Schleife, einem Element gleichzeitig oder in einer einzelnen Anweisung initialisieren. Der Inhalt der folgenden beiden Arrays ist identisch:

    int a[10];
    for (int i = 0; i < 10; ++i)
    {
        a[i] = i + 1;
    }

    int b[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Übergeben von Arrays an Funktionen

Wenn ein Array an eine Funktion übergeben wird, wird es als Zeiger auf das erste Element übergeben, unabhängig davon, ob es sich um ein stapelbasiertes oder heapbasiertes Array handelt. Der Zeiger enthält keine anderen Größen- oder Typinformationen. Dieses Verhalten wird als Zeigerzerfall bezeichnet. Wenn Sie ein Array an eine Funktion übergeben, müssen Sie immer die Anzahl der Elemente in einem separaten Parameter angeben. Dieses Verhalten impliziert auch, dass die Arrayelemente nicht kopiert werden, wenn das Array an eine Funktion übergeben wird. Um zu verhindern, dass die Funktion die Elemente ändert, geben Sie den Parameter als Zeiger auf const Elemente an.

Das folgende Beispiel zeigt eine Funktion, die ein Array und eine Länge akzeptiert. Der Zeiger verweist auf das ursprüngliche Array, nicht auf eine Kopie. Da der Parameter nicht constder Fall ist, kann die Funktion die Arrayelemente ändern.

void process(double *p, const size_t len)
{
    std::cout << "process:\n";
    for (size_t i = 0; i < len; ++i)
    {
        // do something with p[i]
    }
}

Deklarieren und definieren Sie den Arrayparameter p so, dass const er schreibgeschützt innerhalb des Funktionsblocks ist:

void process(const double *p, const size_t len);

Die gleiche Funktion kann auch auf diese Weise deklariert werden, ohne dass sich das Verhalten ändert. Das Array wird weiterhin als Zeiger auf das erste Element übergeben:

// Unsized array
void process(const double p[], const size_t len);

// Fixed-size array. Length must still be specified explicitly.
void process(const double p[1000], const size_t len);

Mehrdimensionale Arrays

Bei Arrays, die von anderen Arrays erstellt werden, handelt es sich um mehrdimensionale Arrays. Diese mehrdimensionalen Arrays werden angegeben, indem nacheinander mehrere Konstantenausdrücke in Klammern gesetzt werden. Ein Beispiel ist diese Deklaration:

int i2[5][7];

Es gibt ein Array vom Typ intan, das konzeptionell in einer zweidimensionalen Matrix von fünf Zeilen und sieben Spalten angeordnet ist, wie in der folgenden Abbildung dargestellt:

Conceptual layout of a multidimensional array.

Das Bild ist ein Raster mit 7 Zellen breit und 5 Zellen hoch. Jede Zelle enthält den Index der Zelle. Der erste Zellindex ist mit 0,0 beschriftet. Die nächste Zelle in dieser Zeile ist 0,1 usw. zur letzten Zelle in dieser Zeile, die 0,6 ist. Die nächste Zeile beginnt mit dem Index 1,0. Die Zelle, nach der ein Index von 1,1 enthalten ist. Die letzte Zelle in dieser Zeile ist 1,6. Dieses Muster wird bis zur letzten Zeile wiederholt, die mit dem Index 4,0 beginnt. Die letzte Zelle in der letzten Zeile weist einen Index von 4,6 auf. :::image-end

Sie können multidimensionierte Arrays deklarieren, die eine Initialisierungsliste aufweisen (wie in Initialisierern beschrieben). In diesen Deklarationen kann der Konstantenausdruck, der die Grenzen für die erste Dimension angibt, weggelassen werden. Beispiel:

// arrays2.cpp
// compile with: /c
const int cMarkets = 4;
// Declare a float that represents the transportation costs.
double TransportCosts[][cMarkets] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

Die vorhergehende Deklaration definiert ein Array, das aus vier Spalten mit je drei Zeilen besteht. Die Zeilen stellen Factorys dar und die Spalten Märkte, die von den Factorys beliefert werden. Die Werte entsprechen den Transportkosten von den Factorys zu den Märkten. Die erste Dimension des Arrays wird ausgelassen, aber der Compiler füllt sie aus, indem er den Initialisierer untersucht.

Die Verwendung des Dereferenzierungsoperators (*) für einen ndimensionalen Arraytyp liefert ein n-1 dimensionales Array. Wenn n 1 ist, wird ein Skalar (oder Arrayelement) zurückgegeben.

C++-Arrays werden in zeilengerichteter Reihenfolge gespeichert. Zeilengerichtete Reihenfolge bedeutet, dass sich der letzte Feldindex am schnellsten unterscheidet.

Beispiel

Sie können auch die Begrenzungsspezifikation für die erste Dimension eines multidimensionalen Arrays in Funktionsdeklarationen weglassen, wie hier gezeigt:

// multidimensional_arrays.cpp
// compile with: /EHsc
// arguments: 3
#include <limits>   // Includes DBL_MAX
#include <iostream>

const int cMkts = 4, cFacts = 2;

// Declare a float that represents the transportation costs
double TransportCosts[][cMkts] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

// Calculate size of unspecified dimension
const int cFactories = sizeof TransportCosts /
                  sizeof( double[cMkts] );

double FindMinToMkt( int Mkt, double myTransportCosts[][cMkts], int mycFacts);

using namespace std;

int main( int argc, char *argv[] ) {
   double MinCost;

   if (argv[1] == 0) {
      cout << "You must specify the number of markets." << endl;
      exit(0);
   }
   MinCost = FindMinToMkt( *argv[1] - '0', TransportCosts, cFacts);
   cout << "The minimum cost to Market " << argv[1] << " is: "
       << MinCost << "\n";
}

double FindMinToMkt(int Mkt, double myTransportCosts[][cMkts], int mycFacts) {
   double MinCost = DBL_MAX;

   for( size_t i = 0; i < cFacts; ++i )
      MinCost = (MinCost < TransportCosts[i][Mkt]) ?
         MinCost : TransportCosts[i][Mkt];

   return MinCost;
}
The minimum cost to Market 3 is: 17.29

Die Funktion FindMinToMkt wird so geschrieben, dass das Hinzufügen neuer Fabriken keine Codeänderungen erfordert, nur eine Neukompilierung.

Initialisieren von Arrays

Arrays von Objekten, die über einen Klassenkonstruktor verfügen, werden vom Konstruktor initialisiert. Wenn in der Initialisierungsliste weniger Elemente vorhanden sind als Elemente im Array, wird der Standardkonstruktor für die neu Standard ing-Elemente verwendet. Wenn kein Standardkonstruktor für die Klasse definiert ist, muss die Initialisierungsliste abgeschlossen sein, d. h. für jedes Element im Array muss ein Initialisierer vorhanden sein.

Betrachten Sie die Point-Klasse, die zwei Konstruktoren definiert:

// initializing_arrays1.cpp
class Point
{
public:
   Point()   // Default constructor.
   {
   }
   Point( int, int )   // Construct from two ints
   {
   }
};

// An array of Point objects can be declared as follows:
Point aPoint[3] = {
   Point( 3, 3 )     // Use int, int constructor.
};

int main()
{
}

Das erste Element von aPoint wird unter Verwendung des Konstruktors Point( int, int ) erstellt; die verbleibenden zwei Elemente werden unter Verwendung des Standardkonstruktors erstellt.

Statische Memberarrays (ob const vorhanden oder nicht) können in ihren Definitionen (außerhalb der Klassendeklaration) initialisiert werden. Beispiel:

// initializing_arrays2.cpp
class WindowColors
{
public:
    static const char *rgszWindowPartList[7];
};

const char *WindowColors::rgszWindowPartList[7] = {
    "Active Title Bar", "Inactive Title Bar", "Title Bar Text",
    "Menu Bar", "Menu Bar Text", "Window Background", "Frame"   };
int main()
{
}

Zugreifen auf Arrayelemente

Sie können auf einzelne Elemente eines Arrays zugreifen, indem Sie den Arrayfeldindex-Operator ([ ]) verwenden. Wenn Sie den Namen eines eindimensionalen Arrays ohne Tiefgestellt verwenden, wird es als Zeiger auf das erste Element des Arrays ausgewertet.

// using_arrays.cpp
int main() {
   char chArray[10];
   char *pch = chArray;   // Evaluates to a pointer to the first element.
   char   ch = chArray[0];   // Evaluates to the value of the first element.
   ch = chArray[3];   // Evaluates to the value of the fourth element.
}

Wenn Sie mehrdimensionale Arrays verwenden, können Sie verschiedene Kombinationen in Ausdrücken verwenden.

// using_arrays_2.cpp
// compile with: /EHsc /W1
#include <iostream>
using namespace std;
int main() {
   double multi[4][4][3];   // Declare the array.
   double (*p2multi)[3];
   double (*p1multi);

   cout << multi[3][2][2] << "\n";   // C4700 Use three subscripts.
   p2multi = multi[3];               // Make p2multi point to
                                     // fourth "plane" of multi.
   p1multi = multi[3][2];            // Make p1multi point to
                                     // fourth plane, third row
                                     // of multi.
}

Im vorherigen Code multi ist ein dreidimensionales Array vom Typ double. Der p2multi Zeiger zeigt auf ein Array vom Typ "Größe double 3". In diesem Beispiel wird das Array mit einem, zwei oder drei Feldindizes verwendet. Obwohl es häufiger ist, alle Tiefstellungen wie in der cout Anweisung anzugeben, ist es manchmal hilfreich, eine bestimmte Teilmenge von Arrayelementen auszuwählen, wie in den folgenden coutAnweisungen gezeigt.

Überlastungsoperator für tiefgestellte Zeichen

Wie bei anderen Operatoren kann der Tiefstellungsoperator ([]) vom Benutzer neu definiert werden. Das Standardverhalten des Indexoperators, wenn er nicht überladen ist, besteht darin, den Arraynamen und den Index unter Verwendung der folgenden Methode zu kombinieren:

*((array_name) + (subscript))

Wie bei allen Weiteren, die Zeigertypen beinhalten, erfolgt die Skalierung automatisch, um die Größe des Typs anzupassen. Der resultierende Wert ist nicht n Byte vom Ursprung des array_nameArrays. Stattdessen ist er das nth-Elementdes Arrays. Weitere Informationen zu dieser Konvertierung finden Sie unter Additive Operatoren.

Entsprechend wird die Adresse für mehrdimensionale Arrays anhand der folgenden Methode abgeleitet:

((array_name) + (subscript1 * max2 * max3 * ... * maxn) + (subscript2 * max3 * ... * maxn) + ... + subscriptn))

Arrays in Ausdrücken

Wenn ein Bezeichner eines Arraytyps in einem anderen Ausdruck als sizeofder Adresse von (&) oder der Initialisierung eines Verweises angezeigt wird, wird er in einen Zeiger auf das erste Arrayelement konvertiert. Beispiel:

char szError1[] = "Error: Disk drive not ready.";
char *psz = szError1;

Der Zeiger psz zeigt auf das erste Element des Arrays szError1. Arrays sind im Gegensatz zu Zeigern nicht modifizierbare l-Werte. Aus diesem Grund ist die folgende Zuordnung unzulässig:

szError1 = psz;

Weitere Informationen

std::array