Suche…
Einführung
Arrays sind Elemente desselben Typs, die sich in benachbarten Speicherstellen befinden. Die Elemente können einzeln durch einen eindeutigen Bezeichner mit einem hinzugefügten Index referenziert werden.
Auf diese Weise können Sie mehrere Variablenwerte eines bestimmten Typs deklarieren und einzeln darauf zugreifen, ohne für jeden Wert eine Variable deklarieren zu müssen.
Arraygröße: Typ sicher zur Kompilierzeit.
#include <stddef.h> // size_t, ptrdiff_t
//----------------------------------- Machinery:
using Size = ptrdiff_t;
template< class Item, size_t n >
constexpr auto n_items( Item (&)[n] ) noexcept
-> Size
{ return n; }
//----------------------------------- Usage:
#include <iostream>
using namespace std;
auto main()
-> int
{
int const a[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
Size const n = n_items( a );
int b[n] = {}; // An array of the same size as a.
(void) b;
cout << "Size = " << n << "\n";
}
Das C-Idiom für die Array-Größe sizeof(a)/sizeof(a[0])
akzeptiert einen Zeiger als Argument und liefert im Allgemeinen ein falsches Ergebnis.
Für C ++ 11
Mit C ++ 11 können Sie Folgendes tun:
std::extent<decltype(MyArray)>::value;
Beispiel:
char MyArray[] = { 'X','o','c','e' };
const auto n = std::extent<decltype(MyArray)>::value;
std::cout << n << "\n"; // Prints 4
Bis 17 ++ C (bevorstehend zum Zeitpunkt des Schreibens) C ++ hatte keine Einbau-Kernsprache oder Standardbibliothek Dienstprogramm die Größe eines Arrays zu erhalten, aber dies kann durch Hindurchleiten der Array unter Bezugnahme auf eine Funktionsschablone implementiert werden, wie oben gezeigt. Fein, aber wichtig: Der Parameter für die Vorlagengröße ist ein size_t
, der mit dem Ergebnistyp der signierten Size
Funktion etwas inkonsistent ist, um den g ++ - Compiler unterzubringen, der manchmal auf size_t
für den Vorlagenabgleich besteht.
Mit C ++ 17 und höher kann man stattdessen std::size
, das auf Arrays spezialisiert ist.
Rohes Array mit dynamischer Größe
// Example of raw dynamic size array. It's generally better to use std::vector.
#include <algorithm> // std::sort
#include <iostream>
using namespace std;
auto int_from( istream& in ) -> int { int x; in >> x; return x; }
auto main()
-> int
{
cout << "Sorting n integers provided by you.\n";
cout << "n? ";
int const n = int_from( cin );
int* a = new int[n]; // ← Allocation of array of n items.
for( int i = 1; i <= n; ++i )
{
cout << "The #" << i << " number, please: ";
a[i-1] = int_from( cin );
}
sort( a, a + n );
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
delete[] a;
}
Ein Programm, das ein Array T a[n];
deklariert T a[n];
Wenn n
zur Laufzeit bestimmt wird, kann es mit bestimmten Compilern kompiliert werden, die C99- Arrays mit variadischer Länge (VLAs) als Spracherweiterung unterstützen. VLAs werden jedoch nicht von Standard C ++ unterstützt. Dieses Beispiel zeigt, wie Sie ein Array mit dynamischer Größe manuell über einen new[]
-Expression zuweisen können.
int* a = new int[n]; // ← Allocation of array of n items.
… Dann verwenden Sie es und heben Sie es schließlich über einen delete[]
-Expression auf:
delete[] a;
Das hier zugewiesene Array hat unbestimmte Werte, es kann jedoch durch einfaches Hinzufügen einer leeren Klammer ()
null initialisiert werden: new int[n]()
. Im Allgemeinen führt dies für einen beliebigen Elementtyp eine Wertinitialisierung durch .
Als Teil einer Funktion in einer Aufrufhierarchie wäre dieser Code nicht ausnahmesicher, da eine Ausnahme vor dem Ausdruck delete[]
(und nach dem new[]
) einen Speicherverlust verursachen würde. Eine Möglichkeit, dieses Problem anzugehen, ist die Automatisierung der Bereinigung, z. B. über einen intelligenten Zeiger std::unique_ptr
. Im Allgemeinen ist es jedoch eine bessere std::vector
, einfach einen std::vector
zu verwenden. Dafür gibt es std::vector
.
Erweitern des Arrays dynamischer Größe mithilfe von std :: vector.
// Example of std::vector as an expanding dynamic size array.
#include <algorithm> // std::sort
#include <iostream>
#include <vector> // std::vector
using namespace std;
int int_from( std::istream& in ) { int x = 0; in >> x; return x; }
int main()
{
cout << "Sorting integers provided by you.\n";
cout << "You can indicate EOF via F6 in Windows or Ctrl+D in Unix-land.\n";
vector<int> a; // ← Zero size by default.
while( cin )
{
cout << "One number, please, or indicate EOF: ";
int const x = int_from( cin );
if( !cin.fail() ) { a.push_back( x ); } // Expands as necessary.
}
sort( a.begin(), a.end() );
int const n = a.size();
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
}
std::vector
ist eine Standardvorlage für Bibliotheksklassen, die die Vorstellung eines Arrays mit variabler Größe bereitstellt. Es kümmert sich um die gesamte Speicherverwaltung, und der Puffer ist zusammenhängend, so dass ein Zeiger auf den Puffer (z. B. &v[0]
oder v.data()
) an API-Funktionen übergeben werden kann, die ein Raw-Array erfordern. Ein vector
kann sogar zur Laufzeit erweitert werden, z. B. über die push_back
push_back, die ein Element anfügt.
Die Komplexität der Folge von n push_back
Operationen, einschließlich des Kopierens oder Verschiebens der push_back
, wird amortisiert O ( n ). Amortisiert: im Durchschnitt.
Intern wird dies normalerweise dadurch erreicht, dass der Vektor seine Puffergröße und Kapazität verdoppelt , wenn ein größerer Puffer benötigt wird. Wenn ein Puffer beispielsweise mit der Größe 1 beginnt und bei n = 17 push_back
Aufrufen wiederholt verdoppelt wird, sind 1 + 2 + 4 + 8 + 16 = 31 Kopiervorgänge erforderlich, was weniger als 2 × n = 34 ist Im Allgemeinen kann die Summe dieser Sequenz 2 × n nicht überschreiten.
Verglichen mit dem Beispiel für ein dynamisches Array mit dynamischer Größe erfordert dieser vector
Code nicht, dass der Benutzer die Anzahl der Elemente vorab bereitstellt (und kennt) muss. Stattdessen wird der Vektor für jeden neuen vom Benutzer angegebenen Elementwert nur nach Bedarf erweitert.
Eine Raw-Array-Matrix mit fester Größe (d. H. Ein 2D-Raw-Array).
// A fixed size raw array matrix (that is, a 2D raw array).
#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
int const n_rows = 3;
int const n_cols = 7;
int const m[n_rows][n_cols] = // A raw array matrix.
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};
for( int y = 0; y < n_rows; ++y )
{
for( int x = 0; x < n_cols; ++x )
{
cout << setw( 4 ) << m[y][x]; // Note: do NOT use m[y,x]!
}
cout << '\n';
}
}
Ausgabe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
C ++ unterstützt keine spezielle Syntax für die Indizierung eines mehrdimensionalen Arrays. Stattdessen wird ein solches Array als Array von Arrays (möglicherweise von Arrays usw.) betrachtet, und für jede Ebene wird die normale Einzelindexnotation [
i
]
verwendet. Im obigen Beispiel bezieht sich m[y]
auf die Zeile y
von m
, wobei y
ein auf Null basierender Index ist. Dann kann diese Zeile wiederum indiziert werden, z m[y][x]
B. m[y][x]
, was sich auf das x
te Element oder die Spalte von Zeile y
bezieht.
Dh der letzte Index variiert am schnellsten, und in der Deklaration ist der Bereich dieses Indexes, der hier die Anzahl der Spalten pro Zeile ist, die letzte und "innerste" Größe.
Da C ++ keine dynamische Unterstützung für Arrays mit dynamischer Größe bietet, wird die dynamische Matrix, außer der dynamischen Zuweisung, häufig als Klasse implementiert. Dann hat die Indizierungsnotation für die rohe Array-Matrix m[y][x]
einige Kosten, entweder durch Offenlegung der Implementierung (sodass z. B. die Ansicht einer transponierten Matrix praktisch unmöglich wird) oder durch Hinzufügen eines zusätzlichen Aufwands und geringfügiger Unbequemlichkeiten, wenn sie ausgeführt werden ein Proxy-Objekt von operator[]
. Die Indizierungsnotation für eine solche Abstraktion kann und wird daher normalerweise unterschiedlich sein, sowohl im Look & Feel als auch in der Reihenfolge der Indizes, z m(x,y)
B. m(x,y)
oder m.at(x,y)
oder m.item(x,y)
.
Eine dynamische Größenmatrix unter Verwendung von std :: vector zur Speicherung.
Leider gibt es seit C ++ 14 keine dynamische Größenmatrixklasse in der C ++ - Standardbibliothek. Matrix - Klassen , die dynamische Größe unterstützt , sind jedoch von einer Reihe von 3 rd Party - Bibliotheken, darunter der Boost - Matrix - Bibliothek (eine Unterbibliothek innerhalb der Boost - Bibliothek).
Wenn Sie keine Abhängigkeit von Boost oder einer anderen Bibliothek wünschen, ist die dynamische Größenmatrix eines armen Mannes in C ++ genau so
vector<vector<int>> m( 3, vector<int>( 7 ) );
... wo vector
ist std::vector
. Die Matrix wird hier erstellt, indem ein Zeilenvektor n- mal kopiert wird, wobei n die Anzahl der Zeilen, hier 3, ist. Sie hat den Vorteil, dass sie die gleiche m[y][x]
-Indexnotation bereitstellt wie eine feste Array-Matrix mit fester Größe, jedoch Dies ist etwas ineffizient, da für jede Zeile eine dynamische Zuweisung erforderlich ist, und es ist ein bisschen unsicher, da eine unbeabsichtigte Größenänderung der Zeile möglich ist.
Ein sicherer und effizienter Ansatz besteht darin, einen einzelnen Vektor als Speicher für die Matrix zu verwenden und den Clientcode ( x , y ) einem entsprechenden Index in diesem Vektor zuzuordnen:
// A dynamic size matrix using std::vector for storage.
//--------------------------------------------- Machinery:
#include <algorithm> // std::copy
#include <assert.h> // assert
#include <initializer_list> // std::initializer_list
#include <vector> // std::vector
#include <stddef.h> // ptrdiff_t
namespace my {
using Size = ptrdiff_t;
using std::initializer_list;
using std::vector;
template< class Item >
class Matrix
{
private:
vector<Item> items_;
Size n_cols_;
auto index_for( Size const x, Size const y ) const
-> Size
{ return y*n_cols_ + x; }
public:
auto n_rows() const -> Size { return items_.size()/n_cols_; }
auto n_cols() const -> Size { return n_cols_; }
auto item( Size const x, Size const y )
-> Item&
{ return items_[index_for(x, y)]; }
auto item( Size const x, Size const y ) const
-> Item const&
{ return items_[index_for(x, y)]; }
Matrix(): n_cols_( 0 ) {}
Matrix( Size const n_cols, Size const n_rows )
: items_( n_cols*n_rows )
, n_cols_( n_cols )
{}
Matrix( initializer_list< initializer_list<Item> > const& values )
: items_()
, n_cols_( values.size() == 0? 0 : values.begin()->size() )
{
for( auto const& row : values )
{
assert( Size( row.size() ) == n_cols_ );
items_.insert( items_.end(), row.begin(), row.end() );
}
}
};
} // namespace my
//--------------------------------------------- Usage:
using my::Matrix;
auto some_matrix()
-> Matrix<int>
{
return
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};
}
#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
Matrix<int> const m = some_matrix();
assert( m.n_cols() == 7 );
assert( m.n_rows() == 3 );
for( int y = 0, y_end = m.n_rows(); y < y_end; ++y )
{
for( int x = 0, x_end = m.n_cols(); x < x_end; ++x )
{
cout << setw( 4 ) << m.item( x, y ); // ← Note: not `m[y][x]`!
}
cout << '\n';
}
}
Ausgabe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Der obige Code ist nicht für die Industrie geeignet: Er soll die grundlegenden Prinzipien aufzeigen und den Bedürfnissen der Schüler entsprechen, die C ++ lernen.
Beispielsweise kann man operator()
Überladungen definieren, um die Indizierungsnotation zu vereinfachen.
Array-Initialisierung
Ein Array ist nur ein Block von sequentiellen Speicherplätzen für einen bestimmten Variablentyp. Arrays werden auf dieselbe Weise wie normale Variablen zugewiesen, jedoch werden an ihren Namen []
eckige Klammern angehängt, die die Anzahl der Elemente enthalten, die in den Arrayspeicher passen.
Das folgende Beispiel eines Arrays verwendet den Typ int
, den Variablennamen arrayOfInts
und die Anzahl der Elemente [5]
, für die das Array Platz hat:
int arrayOfInts[5];
Ein Array kann gleichzeitig deklariert und initialisiert werden
int arrayOfInts[5] = {10, 20, 30, 40, 50};
Bei der Initialisierung eines Arrays durch Auflisten aller seiner Mitglieder ist es nicht erforderlich, die Anzahl der Elemente in die eckigen Klammern einzufügen. Es wird automatisch vom Compiler berechnet. Im folgenden Beispiel sind es 5:
int arrayOfInts[] = {10, 20, 30, 40, 50};
Es ist auch möglich, nur die ersten Elemente zu initialisieren, während mehr Speicherplatz zugewiesen wird. In diesem Fall muss die Länge in Klammern definiert werden. Im Folgenden wird ein Array der Länge 5 mit Teilinitialisierung zugewiesen. Der Compiler initialisiert alle übrigen Elemente mit dem Standardwert des Elementtyps, in diesem Fall Null.
int arrayOfInts[5] = {10,20}; // means 10, 20, 0, 0, 0
Arrays anderer Basisdatentypen können auf dieselbe Weise initialisiert werden.
char arrayOfChars[5]; // declare the array and allocate the memory, don't initialize
char arrayOfChars[5] = { 'a', 'b', 'c', 'd', 'e' } ; //declare and initialize
double arrayOfDoubles[5] = {1.14159, 2.14159, 3.14159, 4.14159, 5.14159};
string arrayOfStrings[5] = { "C++", "is", "super", "duper", "great!"};
Beachten Sie auch, dass beim Zugriff auf Array-Elemente der Elementindex (oder die Position) des Arrays bei 0 beginnt.
int array[5] = { 10/*Element no.0*/, 20/*Element no.1*/, 30, 40, 50/*Element no.4*/};
std::cout << array[4]; //outputs 50
std::cout << array[0]; //outputs 10