Wie deklariere ich ein 2d-Array in C ++ mit new?

527

Wie deklariere ich ein 2D-Array mit new?

Für ein "normales" Array würde ich:

int* ary = new int[Size]

aber

int** ary = new int[sizeY][sizeX]

a) funktioniert nicht / kompiliert und b) erreicht nicht was:

int ary[sizeY][sizeX] 

tut.

user20844
quelle
60
Es funktioniert nur, wenn sizeX konstant ist: int (* ary) [sizeX] = new int [sizeY] [sizeX]; Welches ist der richtige Weg, um ein int [sizeY] [sizeX] zu erstellen und wo der gesamte Speicher zusammenhängend ist. (Ich denke nicht, dass dies eine Antwort wert ist, da wahrscheinlich Ihre Größe X nicht konstant ist
Johannes Schaub - litb
23
Ich kann nicht glauben , das ganze Dutzend Antworten unten sind alle falsch und nicht die Frage beantworten, und doch sind sie alle upvoted. Der obige Kommentar von Johanes Shaub ist die einzig richtige Antwort auf die Frage . Ein 2D-Array und ein Array von Zeigern auf Arrays sind zwei völlig getrennte Dinge, die anscheinend jeder verwechselt.
Bregalad
6
@ JohannesSchaub-litb: Das ist nicht 100% richtig. In diesem Fall funktioniert es zwar, aber es gibt eine Methode, mit der alle Abmessungen variieren, siehe stackoverflow.com/a/29375830/103167
Ben Voigt

Antworten:

749

Ein dynamisches 2D-Array ist im Grunde ein Array von Zeigern auf Arrays . Sie können es mit einer Schleife wie folgt initialisieren:

int** a = new int*[rowCount];
for(int i = 0; i < rowCount; ++i)
    a[i] = new int[colCount];

Das Obige für colCount= 5und rowCount = 4würde Folgendes ergeben:

Geben Sie hier die Bildbeschreibung ein

Mehrdad Afshari
quelle
143
Denken Sie daran, dass alles, was mit zugeordnet newist, auf dem Heap erstellt wird und mit der Zuordnung aufgehoben werden muss. deleteDenken Sie daran und löschen Sie diesen Speicher aus dem Heap, wenn Sie damit fertig sind, um Lecks zu vermeiden.
Kekoa
83
Beachten Sie auch, dass dies ein Array von Zeigern ist. nicht von Arrays. Der Zeiger zeigt wiederum auf Arrays. Wichtig, um die Bedingungen wirklich zu korrigieren, da viele Tutorials auch falsch verstehen. Eine Reihe von Arrays wäre zusammenhängend, was diese nicht ist
Johannes Schaub - litb
4
Ja, ein T [] [N] würde als "Array von Array [N] von T" bezeichnet und wäre ein unvollständiger Typ, während T [] [] ein ungültiger Typ wäre (alle außer den letzten Dimensionen müssen eine bekannte Größe haben ). T [N] [M] ist "Array [N] von Array [M] von T", während T [sizeX] "Array [sizeX] von T" ist, wobei T ein Zeiger auf ein int ist. Das Erstellen eines dynamischen 2D-Arrays funktioniert folgendermaßen: new int [X] [Y]. Es wird ein Array eines zugewiesenen Typs int [X] [Y] erstellt. Dies ist ein "Loch" im
Typensystem
34
Oh mein Gott, das ist völliger Müll, das ist völlig falsch. Dies ist kein 2D-Array. "Ein dynamisches 2D-Array ist im Grunde ein Array von Zeigern auf Arrays." - NOOOO, FFS! T (*ptr)[M] = new T[N][M];ist die richtige Lösung… Keine Anzahl von Arrays von Zeigern wird jemals die gleiche sein wie ein Array von Arrays…
The Paramagnetic Croissant
7
@TheParamagneticCroissant Sie können argumentieren, dass es sich nicht um ein 2D-Array handelt. Es ist wahr. Es kann wie ein 2D-Array indiziert werden, ist jedoch kein 2D-Array. Das Speicherlayout ist tatsächlich im Bild dargestellt. Das Problem mit dieser Anweisung ist, dass sie nicht funktioniert, wenn M nicht konstant ist.
Mehrdad Afshari
300
int** ary = new int[sizeY][sizeX]

sollte sein:

int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
    ary[i] = new int[sizeX];
}

und dann aufräumen wäre:

for(int i = 0; i < sizeY; ++i) {
    delete [] ary[i];
}
delete [] ary;

EDIT: Wie Dietrich Epp in den Kommentaren betonte, ist dies nicht gerade eine leichte Lösung. Ein alternativer Ansatz wäre die Verwendung eines großen Speicherblocks:

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]
Kevin Loney
quelle
56
Es ist etwas schwerer als nötig und weist mehr Blöcke zu, als Sie benötigen. Mehrdimensionale Arrays benötigen nur einen Speicherblock, nicht einen Block pro Zeile. Das Zuweisen von nur einem Block vereinfacht auch die Bereinigung.
Dietrich Epp
11
@ Kevin: Das Zuweisen nur eines einzigen zusammenhängenden Blocks ist der richtige Weg (weniger Auswirkungen auf den Allokator, bessere Lokalität usw.). Aber Sie müssen nicht auf sauberes Abonnement verzichten. Siehe stackoverflow.com/a/29375830/103167
Ben Voigt
9
Sollte es nicht sein i*sizeX+j? Wenn ich mich richtig erinnere, sollte es bei der Hauptreihenfolge Zeile * numColumns + col sein.
Arao6
2
hm, nettes Denken, in der Tat ist es nur eine Frage der Repräsentation - der Rest ist Perspektive. klug
Miro Rodozov
1
@Borna: Im Allgemeinen ist die Verwendung eines einzelnen 2D-Arrays schneller als die Verwendung eines Arrays von Arrays. Das Befolgen von zwei Zeigern kann zum Stillstand der Pipeline führen. Wie immer hängt es von den Zugriffsmustern ab.
Dietrich Epp
211

Obwohl diese beliebte Antwort Ihnen die gewünschte Indizierungssyntax liefert, ist sie doppelt ineffizient: groß und langsam, sowohl räumlich als auch zeitlich. Es gibt einen besseren Weg.

Warum diese Antwort groß und langsam ist

Die vorgeschlagene Lösung besteht darin, ein dynamisches Array von Zeigern zu erstellen und dann jeden Zeiger mit einem eigenen, unabhängigen dynamischen Array zu initialisieren. Der Vorteil dieses Ansatzes besteht darin, dass Sie die gewohnte Indizierungssyntax erhalten. Wenn Sie also den Wert der Matrix an Position x, y ermitteln möchten, sagen Sie:

int val = matrix[ x ][ y ];

Dies funktioniert, weil Matrix [x] einen Zeiger auf ein Array zurückgibt, das dann mit [y] indiziert wird. Brechen sie ab:

int* row = matrix[ x ];
int  val = row[ y ];

Praktisch, ja? Wir mögen unsere [x] [y] -Syntax.

Die Lösung hat jedoch einen großen Nachteil : Sie ist sowohl fett als auch langsam.

Warum?

Der Grund, warum es sowohl fett als auch langsam ist, ist tatsächlich der gleiche. Jede "Zeile" in der Matrix ist ein separat zugewiesenes dynamisches Array. Eine Heap-Zuordnung ist sowohl zeitlich als auch räumlich teuer. Der Allokator benötigt Zeit, um die Zuordnung vorzunehmen, und führt manchmal O (n) -Algorithmen aus, um dies zu tun. Und der Allokator "füllt" jedes Ihrer Zeilenarrays mit zusätzlichen Bytes für die Buchhaltung und Ausrichtung auf. Dieser zusätzliche Platz kostet ... na ja ... zusätzlichen Platz. Der Deallocator wird auch zusätzliche Zeit in Anspruch, wenn Sie die Zuordnung der Matrix aufheben, wodurch jede einzelne Zeilenzuordnung sorgfältig . Bringt mich ins Schwitzen, wenn ich nur daran denke.

Es gibt noch einen anderen Grund, warum es langsam ist. Diese getrennten Zuordnungen neigen dazu, in diskontinuierlichen Teilen des Gedächtnisses zu leben. Eine Zeile befindet sich möglicherweise an der Adresse 1.000, eine andere an der Adresse 100.000 - Sie haben die Idee. Dies bedeutet, dass Sie beim Durchqueren der Matrix wie eine wilde Person durch die Erinnerung springen. Dies führt tendenziell zu Cache-Fehlern, die Ihre Verarbeitungszeit erheblich verlangsamen.

Wenn Sie also unbedingt Ihre niedliche [x] [y] Indizierungssyntax haben müssen, verwenden Sie diese Lösung. Wenn Sie Schnelligkeit und Kleinheit wünschen (und wenn Sie sich nicht für diese interessieren, warum arbeiten Sie in C ++?), Benötigen Sie eine andere Lösung.

Eine andere Lösung

Die bessere Lösung besteht darin, Ihre gesamte Matrix als ein einziges dynamisches Array zuzuweisen und dann (leicht) eine eigene clevere Indizierungsmathematik zu verwenden, um auf Zellen zuzugreifen. Die Indizierungsmathematik ist nur sehr wenig klug; Nein, es ist überhaupt nicht klug: Es ist offensichtlich.

class Matrix
{
    ...
    size_t index( int x, int y ) const { return x + m_width * y; }
};

Mit dieser index()Funktion (von der ich mir vorstelle, dass sie Mitglied einer Klasse ist, weil sie die m_widthIhrer Matrix kennen muss) können Sie auf Zellen in Ihrem Matrix-Array zugreifen. Das Matrixarray wird wie folgt zugeordnet:

array = new int[ width * height ];

Das Äquivalent dazu in der langsamen, fetten Lösung:

array[ x ][ y ]

... ist das in der schnellen, kleinen Lösung:

array[ index( x, y )]

Traurig, ich weiß. Aber du wirst dich daran gewöhnen. Und Ihre CPU wird es Ihnen danken.

OldPeculier
quelle
5
@Nein, ich habe eine Art Lösung entworfen, ohne eine bestimmte vorzuschreiben. Weitere Details könnten folgendermaßen aussehen: class Matrix { int* array; int m_width; public: Matrix( int w, int h ) : m_width( w ), array( new int[ w * h ] ) {} ~Matrix() { delete[] array; } int at( int x, int y ) const { return array[ index( x, y ) ]; } protected: int index( int x, int y ) const { return x + m_width * y; } };Wenn Sie diesen Code korrigieren, ist dies möglicherweise sinnvoll und gibt Aufschluss über die obige Antwort.
OldPeculier
4
Ich mag diese Lösung sehr, ist sie auch auf 3-Dimensionen-Arrays anwendbar? Ich denke so etwas: (x + m_width * y) + (m_width * m_height * z)
Ulrar
3
Das Hauptproblem bei dieser Lösung besteht darin, dass für jeden Index zusätzliche Berechnungen durchgeführt werden. Es wird schlimmer, wenn Sie die Indexberechnung in eine Funktion einfügen, die zusätzlichen Aufwand verursacht. Erwägen Sie zumindest die Verwendung von Makros oder Inline-Funktionen , um den Overhead zu reduzieren. Ein Beispielmakro für C ++: #define ROW_COL_TO_INDEX(row, col, num_cols) (row*num_cols + col)Dann können Sie es als verwenden. int COLS = 4; A[ ROW_COL_TO_INDEX(r, c, COLS) ] = 75; Der Overhead wirkt sich wirklich aus, wenn wir Matrixmultiplikationen durchführen, die für Strassens Algorithmus die Komplexität O (n ^ 3) oder O (n ^ 2.81) haben .
Ash Ketchum
5
@AshKetchum Inlining (oder vielleicht Makrosubstitution) ist sinnvoll zu optimieren, aber wie ist die kompilierte Berechnung komplexer als das, was getan werden muss, um die Adresse von a [x] [y] aufzulösen?
Dronz
1
@Dronz Mit a[x][y], was Sie tatsächlich tun, ist *(*(a + x) + y): zwei Ergänzungen und zwei Speicherabrufe. Mit a[index(x, y)], das tun , was man eigentlich sind *(a + x + w*y): zwei Zugaben, eine Multiplikation und ein Speicher holen. Letzteres ist aus den in dieser Antwort genannten Gründen oft vorzuziehen (dh es lohnt sich, den zusätzlichen Speicherabruf mit einer Multiplikation zu tauschen, insbesondere weil die Daten nicht fragmentiert sind und Sie daher keinen Cache verpassen).
Boris Dalstein
118

In C ++ 11 ist Folgendes möglich:

auto array = new double[M][N]; 

Auf diese Weise wird der Speicher nicht initialisiert. Um es zu initialisieren, gehen Sie stattdessen folgendermaßen vor:

auto array = new double[M][N]();

Beispielprogramm (kompilieren mit "g ++ -std = c ++ 11"):

#include <iostream>
#include <utility>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;

int main()
{
    const auto M = 2;
    const auto N = 2;

    // allocate (no initializatoin)
    auto array = new double[M][N];

    // pollute the memory
    array[0][0] = 2;
    array[1][0] = 3;
    array[0][1] = 4;
    array[1][1] = 5;

    // re-allocate, probably will fetch the same memory block (not portable)
    delete[] array;
    array = new double[M][N];

    // show that memory is not initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }
    cout << endl;

    delete[] array;

    // the proper way to zero-initialize the array
    array = new double[M][N]();

    // show the memory is initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }

    int info;
    cout << abi::__cxa_demangle(typeid(array).name(),0,0,&info) << endl;

    return 0;
}

Ausgabe:

2 4 
3 5 

0 0 
0 0 
double (*) [2]
Mohammad Alaggan
quelle
3
Ich muss dies in einer Klasse tun, daher kann ich auto nicht verwenden. Was wäre der richtige Typ für das Array?
Peter Smit
3
Können Sie dies dann verwenden:using arr2d = double(*)[2]; arr2d array = new double[M][N];
Mohammad Alaggan
3
+1: Das hat das OP verlangt. Der richtige Typ hierfür ist entweder double (*)[M][N]oder double(*)[][N]mit M, wobei N konstante Ausdrücke sind.
Fozi
58
Das Problem bei dieser Lösung ist, dass die Dimensionen kein Laufzeitwert sein können, sondern zur Kompilierungszeit bekannt sein sollten.
Legends2k
4
@vsoftco Ja, tatsächlich ging es jedoch speziell darum, beide Dimensionen zur Kompilierungszeit unbekannt zu haben.
Legends2k
58

Ich gehe von Ihrem statischen Array-Beispiel aus, dass Sie ein rechteckiges Array und kein gezacktes wollen. Sie können Folgendes verwenden:

int *ary = new int[sizeX * sizeY];

Dann können Sie auf folgende Elemente zugreifen:

ary[y*sizeX + x]

Vergessen Sie nicht, delete [] zu verwenden ary.

Isvara
quelle
1
Dies ist ein guter Weg, um es zu tun. Sie können auch den Vektor <int> mit der Größe sizeX * sizeY ausführen, um zusätzliche Sicherheit zu gewährleisten.
Dietrich Epp
4
Das Beste ist, diesen Code in eine Klasse zu packen - Sie können eine Bereinigung im Destruktor durchführen und die Methoden get (x, y) und set (x, y, val) implementieren, anstatt den Benutzer zu zwingen, die Multiplikation selbst durchzuführen . Die Implementierung von operator [] ist schwieriger, aber ich glaube, dass dies möglich ist.
Tadeusz Kopec
47

Es gibt zwei allgemeine Techniken, die ich in C ++ 11 und höher empfehlen würde, eine für die Dimensionen der Kompilierungszeit und eine für die Laufzeit. Bei beiden Antworten wird davon ausgegangen, dass Sie einheitliche, zweidimensionale Arrays (keine gezackten) wünschen.

Zeitdimensionen kompilieren

Verwenden Sie ein std::arrayvon std::arrayund verwenden Sie es dann new, um es auf den Haufen zu legen:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Dies funktioniert wiederum nur, wenn die Größen der Dimensionen zur Kompilierungszeit bekannt sind.

Laufzeitdimensionen

Der beste Weg, um ein zweidimensionales Array mit Größen zu erstellen, die nur zur Laufzeit bekannt sind, besteht darin, es in eine Klasse zu packen. Die Klasse weist ein 1d-Array zu und überlastet es dannoperator [] es , um die Indizierung für die erste Dimension bereitzustellen. Dies funktioniert, weil in C ++ ein 2D-Array Zeilenmajor ist:

 Matrix in logischer Form und eindimensionaler Form dargestellt

(Entnommen aus http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/ )

Eine zusammenhängende Speicherfolge ist aus Leistungsgründen gut und auch leicht zu bereinigen. Hier ist eine Beispielklasse, die viele nützliche Methoden auslässt, aber die Grundidee zeigt:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

Also erstellen wir ein Array mit std::make_unique<int[]>(rows * columns)Einträgen. Wir überladen, operator []wodurch die Zeile für uns indiziert wird. Es gibt ein int *Zeichen zurück, das auf den Anfang der Zeile zeigt, das dann wie gewohnt für die Spalte dereferenziert werden kann. Beachten Sie, dass das make_uniqueerste Mal in C ++ 14 ausgeliefert wird, Sie es jedoch bei Bedarf in C ++ 11 mehrfach ausfüllen können.

Es ist auch üblich, dass diese Arten von Strukturen ebenfalls überlastet operator()werden:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technisch habe ich hier nicht verwendet new, aber es ist trivial, von std::unique_ptr<int[]>zu zu wechseln int *und new/ zu verwenden delete.

Levi Morrison
quelle
Wäre es möglich, die Zeilen- und Spaltenvorlagenparameter festzulegen?
Janus Troelsen
1
Wenn Sie die Dimensionen zur Kompilierungszeit kennen (was Sie tun, wenn Sie Vorlagenparameter verwenden), empfehle ich die Verwendung eines std::arrayvon std::arrays : std::array<std::array<int, columns> rows>.
Levi Morrison
1
Darf ich sagen, dass dies eine raffinierte / moderne Antwort ist, deren Philosophie der Antwort von @kamshi (in Bezug auf LOC und Konzepte einfacher) sehr ähnlich ist?
KcFnMi
Sie sind in Bezug auf die zugrunde liegende Technik ziemlich vergleichbar: Es gibt ein einziges Array, das alle Werte in allen Dimensionen enthält. Geben Sie dann irgendwie Zeiger auf den Anfang jeder Zeile zurück. In der Praxis verfügt die Klasse normalerweise über nützlichere Methoden, möglicherweise über Kopierkonstruktor- und Kopierzuweisungsoperatoren, über assertsDebug-Builds zur Überprüfung von Speicherzugriffen usw. Diese Ergänzungen erleichtern und erleichtern die Arbeit im Allgemeinen.
Levi Morrison
1
@KcFnMi, wenn Sie sich für die Antwort von kamshi entscheiden, lesen Sie unbedingt Bens Kommentar über die Verwendung von make_uniquestatt new/delete.
Levi Morrison
31

Diese Frage hat mich nervt - es ist ein häufig genug auftretendes Problem, dass es bereits eine gute Lösung geben sollte, etwas Besseres als der Vektorvektor oder das Rollen Ihrer eigenen Array-Indizierung.

Wenn in C ++ etwas vorhanden sein sollte, dies aber nicht ist, sollten Sie zunächst nach boost.org suchen . Dort fand ich die Boost Multidimensional Array Library ,multi_array . Es enthält sogar eine multi_array_refKlasse, mit der Sie Ihren eigenen eindimensionalen Array-Puffer umschließen können.

Mark Ransom
quelle
5
Ich verstehe Ihr Argument, ich persönlich verstehe nicht, warum es so schwierig sein muss, ehrlich gesagt ist dies der Grund, warum wir so viele Programmierer an Java verlieren, da es einfach sofort funktioniert! Dies sind Grundfunktionen, mit denen C ++ - Leute Zeit verlieren!
Oliver
Ich könnte nur hinzufügen, ich finde das die beste Lösung, aber ich denke, für einige Leute braucht es viel Gehirn, um alle Schritte zu verstehen, besonders für Anfänger ...;)! Ich sehe, dass 80% der C ++ - Programmierer versagen, wenn sie typisierte Inhalte sehen.
Oliver
1
@OliverStutz das ist die Gefahr, der Erste zu sein. Die neuesten C ++ - Standards haben verzweifelt versucht, die Dinge weniger belastend zu machen. Mein Favorit ist das autoSchlüsselwort. Ich bin überrascht, dass sie nicht versucht haben, 2D-Arrays in Angriff zu nehmen, zumal Boost bereits den Weg gezeigt hat.
Mark Ransom
Es ist lustig, wie in der gesamten Entwicklungsautomatisierung geschlafen wurde. Jetzt ist Java der einzige einfache Vorteil. Ich möchte wirklich, dass c ++ einen Sprung macht, es ist seit jeher eine mächtige und mächtige Sprache ... warum ein Lichtschwert haben, wenn Sie es nicht benutzen!
Oliver
2
Was mich noch mehr nervt, ist, wie weit C ++ in dieser Hinsicht hinter C liegt: C99 ermöglicht echte mehrdimensionale Arrays, die auf dem Heap mit zur Laufzeit definierten Dimensionen zugewiesen sind, und C ++ 17 kommt immer noch nicht an das heran, was C99 zulässt ...
cmaster - wieder Monica
27

Warum nicht STL: vector verwenden? So einfach, und Sie müssen den Vektor nicht löschen.

int rows = 100;
int cols = 200;
vector< vector<int> > f(rows, vector<int>(cols));
f[rows - 1][cols - 1] = 0; // use it like arrays

Sie können die 'Arrays' auch initialisieren. Geben Sie einfach einen Standardwert ein

const int DEFAULT = 1234;
vector< vector<int> > f(rows, vector<int>(cols, DEFAULT));

Quelle: Wie erstelle ich 2, 3 (oder mehr) dimensionale Arrays in C / C ++?

Doctorlai
quelle
1
beste Lösung IMHO
vipin8169
1
Dies ist keine gute Lösung, wenn ich STL aufgrund von Speicherbeschränkungen nicht laden möchte.
Katta
2
@katta Die meisten nicht trivialen C ++ - Programme verwenden ohnehin STL. Dies ist also eine gute Lösung, nur nicht für einige kleinere Fälle, einschließlich Ihrer.
Zar Shardan
Was ich nicht verstehe, ist, warum so viele Leute den ersten Index als Zeilen und den zweiten als Spalten betrachten. Rebellion gegen XY-Koordinatendiagramme im Matheunterricht?
Dronz
2
@Dronz Das liegt daran, dass dies das C ++ - Speichermodell ist - die Spalten sind im Speicher zusammenhängend, nicht die Zeilen. In Fortran ist es umgekehrt.
Levi Morrison
16

Ein 2D-Array ist im Grunde ein 1D-Array von Zeigern, wobei jeder Zeiger auf ein 1D-Array zeigt, das die tatsächlichen Daten enthält.

Hier ist N Zeile und M Spalte.

dynamische Zuordnung

int** ary = new int*[N];
  for(int i = 0; i < N; i++)
      ary[i] = new int[M];

füllen

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      ary[i][j] = i;

drucken

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      std::cout << ary[i][j] << "\n";

kostenlos

for(int i = 0; i < N; i++)
    delete [] ary[i];
delete [] ary;
akshay_rahar
quelle
14

Wie ordne ich ein zusammenhängendes mehrdimensionales Array in GNU C ++ zu? Es gibt eine GNU-Erweiterung, mit der die "Standard" -Syntax funktioniert.

Es scheint, dass das Problem vom Operator new [] kommt. Stellen Sie sicher, dass Sie stattdessen den Operator new verwenden:

double (* in)[n][n] = new (double[m][n][n]);  // GNU extension

Und das ist alles: Sie erhalten ein C-kompatibles mehrdimensionales Array ...

etham
quelle
Welchen Compiler verwenden Sie? Die Array-Syntax wird kompiliert und läuft einwandfrei mit g ++ 4.6.4 und 4.7.3. Ich erhalte nur eine Warnung zum letzten] vor dem =, dass "berechneter Wert nicht verwendet wird" oder "Anweisung hat keine Auswirkung". Wenn ich jedoch g ++ 4.8.1 verwende (angeblich vollständig c ++ 11-kompatibel), werden Fehler auf n und o ausgegeben, die nicht konstant sind. "Die Arraygröße im Operator new muss konstant sein" und zeigt auf das letzte] in der Zeile.
jbo5112
@cmaster double (*in)[m][n] = (double (*)[m][n])new double[k*m*n];funktioniert auch nicht. Ich erhalte C2057- und C2540-Fehler, nda diese zur Kompilierungszeit nicht bekannt sind. Ich verstehe nicht, warum ich das nicht kann, weil der Speicher richtig zugewiesen ist und es nur Zeiger sind , um diesen Speicher bequem zu handhaben. (VS 2010)
user1234567
2
@ user3241228 hat gccmich getäuscht, als ich dies schrieb: Die Lieferung -std=c++11reicht nicht aus, um die strikte Standardkonformität einzuschalten, -pedantic-errorsist ebenfalls erforderlich. Akzeptiert ohne das spätere Flag gccdie Besetzung gerne, obwohl sie tatsächlich nicht dem C ++ - Standard entspricht. Nach dem, was ich jetzt weiß, kann ich nur raten, auf C zurückzugreifen, wenn ich Dinge mache, die stark von mehrdimensionalen Arrays abhängen. C99 ist in dieser Hinsicht viel leistungsfähiger als selbst C ++ 17.
cmaster
@cmaster dynamisch zugewiesene VLAs sind sowieso syntaktischer Zucker ... sie sind gut in C, weil es nichts anderes gibt, aber C ++ hat besseren syntaktischen Zucker :)
MM
1
@MM Schade, dass C ++ keinen syntaktischen Zucker für ein echtes, aufeinanderfolgendes mehrdimensionales Array hat, das auf dem Heap mit Größen zugewiesen ist, die nur zur Laufzeit bekannt sind. Solange Sie dies nicht benötigen, ist syntaktischer C ++ - Zucker in Ordnung. Aber wenn Sie all das brauchen, schlägt sogar FORTRAN C ++ ...
cmaster - stellen Sie Monica
13

typedef ist dein Freund

Nachdem ich viele der anderen Antworten durchgesehen hatte, stellte ich fest, dass eine tiefere Erklärung angebracht ist, da viele der anderen Antworten entweder unter Leistungsproblemen leiden oder Sie dazu zwingen, ungewöhnliche oder lästige Syntax zu verwenden, um das Array zu deklarieren oder auf das Array zuzugreifen Elemente (oder alle oben genannten).

Bei dieser Antwort wird zunächst davon ausgegangen, dass Sie die Dimensionen des Arrays zur Kompilierungszeit kennen. Wenn Sie dies tun, ist dies die beste Lösung, da beide die beste Leistung bieten Verwendung der Standard-Array-Syntax für den Zugriff auf die Array-Elemente ermöglicht .

Der Grund für die beste Leistung liegt darin, dass alle Arrays als zusammenhängender Speicherblock zugewiesen werden, was bedeutet, dass Sie wahrscheinlich weniger Seitenfehler und eine bessere räumliche Lokalität haben. Das Zuweisen in einer Schleife kann dazu führen, dass die einzelnen Arrays auf mehreren nicht zusammenhängenden Seiten im virtuellen Speicherbereich verstreut sind, da die Zuordnungsschleife (möglicherweise mehrmals) durch andere Threads oder Prozesse unterbrochen werden kann oder einfach aufgrund des Ermessens der Allokator füllt kleine, leere Speicherblöcke aus, die zufällig verfügbar sind.

Die anderen Vorteile sind eine einfache Deklarationssyntax und eine Standard-Array-Zugriffssyntax.

In C ++ mit new:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

typedef double (array5k_t)[5000];

array5k_t *array5k = new array5k_t[5000];

array5k[4999][4999] = 10;
printf("array5k[4999][4999] == %f\n", array5k[4999][4999]);

return 0;
}

Oder C-Stil mit Calloc:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

typedef double (*array5k_t)[5000];

array5k_t array5k = calloc(5000, sizeof(double)*5000);

array5k[4999][4999] = 10;
printf("array5k[4999][4999] == %f\n", array5k[4999][4999]);

return 0;
}
Robert S. Barnes
quelle
1
Der Zugriff über das Ende eines Arrays hinaus führt garantiert nicht zu einem Fehler. Wenn Sie Glück haben, stürzt das Programm einfach ab. Sie befinden sich definitiv im Bereich undefinierten Verhaltens.
Michael Kristofik
Richtig, obwohl der Zweck dieses Beispiels eigentlich nur darin besteht, zu zeigen, wie typedef und new zusammen verwendet werden, um ein 2D-Array zu deklarieren.
Robert S. Barnes
1
Ihre Antwort hat mir sehr gut gefallen. Ich selbst war ein Verfechter von typedef gewesen.
Fooo
12

Dieses Problem hat mich 15 Jahre lang beschäftigt, und alle angebotenen Lösungen waren für mich nicht zufriedenstellend. Wie erstellt man ein dynamisches mehrdimensionales Array zusammenhängend im Speicher? Heute habe ich endlich die Antwort gefunden. Mit dem folgenden Code können Sie genau das tun:

#include <iostream>

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cerr << "You have to specify the two array dimensions" << std::endl;
        return -1;
    }

    int sizeX, sizeY;

    sizeX = std::stoi(argv[1]);
    sizeY = std::stoi(argv[2]);

    if (sizeX <= 0)
    {
        std::cerr << "Invalid dimension x" << std::endl;
        return -1;
    }
    if (sizeY <= 0)
    {
        std::cerr << "Invalid dimension y" << std::endl;
        return -1;
    }

    /******** Create a two dimensional dynamic array in continuous memory ******
     *
     * - Define the pointer holding the array
     * - Allocate memory for the array (linear)
     * - Allocate memory for the pointers inside the array
     * - Assign the pointers inside the array the corresponding addresses
     *   in the linear array
     **************************************************************************/

    // The resulting array
    unsigned int** array2d;

    // Linear memory allocation
    unsigned int* temp = new unsigned int[sizeX * sizeY];

    // These are the important steps:
    // Allocate the pointers inside the array,
    // which will be used to index the linear memory
    array2d = new unsigned int*[sizeY];

    // Let the pointers inside the array point to the correct memory addresses
    for (int i = 0; i < sizeY; ++i)
    {
        array2d[i] = (temp + i * sizeX);
    }



    // Fill the array with ascending numbers
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            array2d[y][x] = x + y * sizeX;
        }
    }



    // Code for testing
    // Print the addresses
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << std::hex << &(array2d[y][x]) << ' ';
        }
    }
    std::cout << "\n\n";

    // Print the array
    for (int y = 0; y < sizeY; ++y)
    {
        std::cout << std::hex << &(array2d[y][0]) << std::dec;
        std::cout << ": ";
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << array2d[y][x] << ' ';
        }
        std::cout << std::endl;
    }



    // Free memory
    delete[] array2d[0];
    delete[] array2d;
    array2d = nullptr;

    return 0;
}

Wenn Sie das Programm mit den Werten sizeX = 20 und sizeY = 15 aufrufen, lautet die Ausgabe wie folgt:

0x603010 0x603014 0x603018 0x60301c 0x603020 0x603024 0x603028 0x60302c 0x603030 0x603034 0x603038 0x60303c 0x603040 0x603044 0x603048 0x60304c 0x603050 0x603054 0x603058 0x60305c 0x603060 0x603064 0x603068 0x60306c 0x603070 0x603074 0x603078 0x60307c 0x603080 0x603084 0x603088 0x60308c 0x603090 0x603094 0x603098 0x60309c 0x6030a0 0x6030a4 0x6030a8 0x6030ac 0x6030b0 0x6030b4 0x6030b8 0x6030bc 0x6030c0 0x6030c4 0x6030c8 0x6030cc 0x6030d0 0x6030d4 0x6030d8 0x6030dc 0x6030e0 0x6030e4 0x6030e8 0x6030ec 0x6030f0 0x6030f4 0x6030f8 0x6030fc 0x603100 0x603104 0x603108 0x60310c 0x603110 0x603114 0x603118 0x60311c 0x603120 0x603124 0x603128 0x60312c 0x603130 0x603134 0x603138 0x60313c 0x603140 0x603144 0x603148 0x60314c 0x603150 0x603154 0x603158 0x60315c 0x603160 0x603164 0x603168 0x60316c 0x603170 0x603174 0x603178 0x60317c 0x603180 0x603184 0x603188 0x60318c 0x603190 0x603194 0x603198 0x60319c 0x6031a0 0x6031a4 0x6031a8 0x6031ac 0x6031b0 0x6031b4 0x6031b8 0x6031bc 0x6031c0 0x6031c4 0x6031c8 0x6031cc 0x6031d0 0x6031d4 0x6031d8 0x6031dc 0x6031e0 0x6031e4 0x6031e8 0x6031ec 0x6031f0 0x6031f4 0x6031f8 0x6031fc 0x603200 0x603204 0x603208 0x60320c 0x603210 0x603214 0x603218 0x60321c 0x603220 0x603224 0x603228 0x60322c 0x603230 0x603234 0x603238 0x60323c 0x603240 0x603244 0x603248 0x60324c 0x603250 0x603254 0x603258 0x60325c 0x603260 0x603264 0x603268 0x60326c 0x603270 0x603274 0x603278 0x60327c 0x603280 0x603284 0x603288 0x60328c 0x603290 0x603294 0x603298 0x60329c 0x6032a0 0x6032a4 0x6032a8 0x6032ac 0x6032b0 0x6032b4 0x6032b8 0x6032bc 0x6032c0 0x6032c4 0x6032c8 0x6032cc 0x6032d0 0x6032d4 0x6032d8 0x6032dc 0x6032e0 0x6032e4 0x6032e8 0x6032ec 0x6032f0 0x6032f4 0x6032f8 0x6032fc 0x603300 0x603304 0x603308 0x60330c 0x603310 0x603314 0x603318 0x60331c 0x603320 0x603324 0x603328 0x60332c 0x603330 0x603334 0x603338 0x60333c 0x603340 0x603344 0x603348 0x60334c 0x603350 0x603354 0x603358 0x60335c 0x603360 0x603364 0x603368 0x60336c 0x603370 0x603374 0x603378 0x60337c 0x603380 0x603384 0x603388 0x60338c 0x603390 0x603394 0x603398 0x60339c 0x6033a0 0x6033a4 0x6033a8 0x6033ac 0x6033b0 0x6033b4 0x6033b8 0x6033bc 0x6033c0 0x6033c4 0x6033c8 0x6033cc 0x6033d0 0x6033d4 0x6033d8 0x6033dc 0x6033e0 0x6033e4 0x6033e8 0x6033ec 0x6033f0 0x6033f4 0x6033f8 0x6033fc 0x603400 0x603404 0x603408 0x60340c 0x603410 0x603414 0x603418 0x60341c 0x603420 0x603424 0x603428 0x60342c 0x603430 0x603434 0x603438 0x60343c 0x603440 0x603444 0x603448 0x60344c 0x603450 0x603454 0x603458 0x60345c 0x603460 0x603464 0x603468 0x60346c 0x603470 0x603474 0x603478 0x60347c 0x603480 0x603484 0x603488 0x60348c 0x603490 0x603494 0x603498 0x60349c 0x6034a0 0x6034a4 0x6034a8 0x6034ac 0x6034b0 0x6034b4 0x6034b8 0x6034bc 

0x603010: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
0x603060: 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 
0x6030b0: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 
0x603100: 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 
0x603150: 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 
0x6031a0: 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 
0x6031f0: 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 
0x603240: 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 
0x603290: 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 
0x6032e0: 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 
0x603330: 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 
0x603380: 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 
0x6033d0: 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 
0x603420: 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 
0x603470: 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

Wie Sie sehen können, liegt das mehrdimensionale Array zusammenhängend im Speicher, und keine zwei Speicheradressen überlappen sich. Selbst die Routine zum Freigeben des Arrays ist einfacher als die Standardmethode zum dynamischen Zuweisen von Speicher für jede einzelne Spalte (oder Zeile, je nachdem, wie Sie das Array anzeigen). Da das Array im Wesentlichen aus zwei linearen Arrays besteht, müssen (und können) nur diese beiden freigegeben werden.

Diese Methode kann mit demselben Konzept um mehr als zwei Dimensionen erweitert werden. Ich werde es hier nicht tun, aber wenn Sie die Idee dahinter haben, ist es eine einfache Aufgabe.

Ich hoffe, dieser Code wird Ihnen genauso helfen, wie er mir geholfen hat.

Kamshi
quelle
1
Es gibt noch eine zusätzliche Reihe von Zeigern. Code, der das Array verwendet, muss die zusätzliche Indirektionsebene ausführen, da dies nicht angenommen werden kann array2d[i] = buffer + i * sizeX. Dies hilft also in geringem Maße, aber im Code, der das Array verwendet, kann der Compiler nicht einfach Zeiger inkrementieren, um das Array zu scannen.
Peter Cordes
4
Ja, genau so geht das. Aber es ist die C-Methode, in C ++ würden wir make_unique<int[]>(sizeX*sizeY)den zusammenhängenden Speicher make_unique<int*[]>(sizeX)einrichten und den Speicher für die Zeiger einrichten (die auf die gleiche Weise zugewiesen werden sollten, wie Sie sie zeigen). Dies befreit Sie von der Notwendigkeit, delete[]am Ende zweimal anzurufen .
Ben Voigt
Diese Antwort macht für mich eine Menge Sinn, noch mehr angesichts des Kommentars von @BenVoigt. Das zusätzliche Array von Zeigern, auf die sich @PeterCordes bezieht, oder temp? In Anbetracht der Vorteile (Continuos 2d-Array mit unbekannten Dimensionen zur Kompilierungszeit) bin ich mir nicht sicher, ob es mir wichtig ist, dass es baumelt. Ich habe nicht verstanden, was @PeterCordes bedeutet extra layer of indirection, was ist das? Warum die Klammer array2d[i] = (temp + i * sizeX);
KcFnMi
delete [] array2d [0] ist dasselbe wie delete [] temp?
KcFnMi
6

Der Zweck dieser Antwort besteht nicht darin, etwas Neues hinzuzufügen, das die anderen noch nicht behandelt haben, sondern die Antwort von @Kevin Loney zu erweitern.

Sie können die Lightweight-Deklaration verwenden:

int *ary = new int[SizeX*SizeY]

und die Zugriffssyntax lautet:

ary[i*SizeY+j]     // ary[i][j]

Dies ist jedoch für die meisten umständlich und kann zu Verwirrung führen. Sie können ein Makro also wie folgt definieren:

#define ary(i, j)   ary[(i)*SizeY + (j)]

Jetzt können Sie mit der sehr ähnlichen Syntax auf das Array zugreifen ary(i, j) // means ary[i][j] . Dies hat den Vorteil, einfach und schön zu sein, und gleichzeitig ist die Verwendung von Ausdrücken anstelle der Indizes einfacher und weniger verwirrend.

Um beispielsweise auf ary [2 + 5] [3 + 8] zuzugreifen, können Sie ary(2+5, 3+8)anstelle des komplex aussehenden schreiben , ary[(2+5)*SizeY + (3+8)]dh es werden Klammern gespeichert und die Lesbarkeit verbessert.

Vorsichtsmaßnahmen:

  • Obwohl die Syntax sehr ähnlich ist, ist sie NICHT dieselbe.
  • Wenn Sie das Array an andere Funktionen übergeben, SizeYmuss es mit demselben Namen übergeben werden (oder stattdessen als globale Variable deklariert werden).

Wenn Sie das Array in mehreren Funktionen verwenden müssen, können Sie SizeY auch wie folgt als weiteren Parameter in die Makrodefinition einfügen:

#define ary(i, j, SizeY)  ary[(i)*(SizeY)+(j)]

Du hast die Idee. Natürlich wird dies zu lang, um nützlich zu sein, aber es kann immer noch die Verwechslung von + und * verhindern.

Dies wird definitiv nicht empfohlen und wird von den meisten erfahrenen Benutzern als schlechte Praxis verurteilt, aber ich konnte nicht widerstehen, es wegen seiner Eleganz zu teilen.

Bearbeiten:
Wenn Sie eine tragbare Lösung wünschen, die für eine beliebige Anzahl von Arrays funktioniert, können Sie diese Syntax verwenden:

#define access(ar, i, j, SizeY) ar[(i)*(SizeY)+(j)]

Anschließend können Sie mithilfe der Zugriffssyntax ein beliebiges Array mit einer beliebigen Größe an den Aufruf weitergeben:

access(ary, i, j, SizeY)      // ary[i][j]

PS: Ich habe diese getestet und die gleiche Syntax funktioniert (sowohl als l-Wert als auch als r-Wert) auf g ++ 14- und g ++ 11-Compilern.

Zargles
quelle
4

Versuchen Sie Folgendes:

int **ary = new int* [sizeY];
for (int i = 0; i < sizeY; i++)
    ary[i] = new int[sizeX];
Stanigator
quelle
2

Hier habe ich zwei Möglichkeiten. Der erste zeigt das Konzept eines Arrays von Arrays oder eines Zeigers von Zeigern. Ich bevorzuge die zweite, weil die Adressen zusammenhängend sind, wie Sie auf dem Bild sehen können.

Geben Sie hier die Bildbeschreibung ein

#include <iostream>

using namespace std;


int main(){

    int **arr_01,**arr_02,i,j,rows=4,cols=5;

    //Implementation 1
    arr_01=new int*[rows];

    for(int i=0;i<rows;i++)
        arr_01[i]=new int[cols];

    for(i=0;i<rows;i++){
        for(j=0;j<cols;j++)
            cout << arr_01[i]+j << " " ;
        cout << endl;
    }


    for(int i=0;i<rows;i++)
        delete[] arr_01[i];
    delete[] arr_01;


    cout << endl;
    //Implementation 2
    arr_02=new int*[rows];
    arr_02[0]=new int[rows*cols];
    for(int i=1;i<rows;i++)
        arr_02[i]=arr_02[0]+cols*i;

    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++)
            cout << arr_02[i]+j << " " ;
        cout << endl;
    }

    delete[] arr_02[0];
    delete[] arr_02;


    return 0;
}
Emarazz
quelle
1

Wenn Ihr Projekt CLI (Common Language Runtime Support) ist , dann:

Sie können die Array-Klasse verwenden, nicht die, die Sie beim Schreiben erhalten:

#include <array>
using namespace std;

Mit anderen Worten, nicht die nicht verwaltete Array-Klasse, die Sie erhalten, wenn Sie den Standard-Namespace verwenden und den Array-Header einschließen, nicht die nicht verwaltete Array-Klasse, die im Standard-Namespace und im Array-Header definiert ist, sondern das verwaltete Klassenarray der CLI.

Mit dieser Klasse können Sie ein Array mit einem beliebigen Rang erstellen .

Der folgende Code erstellt ein neues zweidimensionales Array aus 2 Zeilen und 3 Spalten vom Typ int, und ich nenne es "arr":

array<int, 2>^ arr = gcnew array<int, 2>(2, 3);

Jetzt können Sie auf Elemente im Array zugreifen, indem Sie sie benennen und nur eine quadratische Klammer schreiben. Fügen Sie []in ihnen die Zeile und Spalte hinzu und trennen Sie sie durch das Komma ,.

Der folgende Code greift auf ein Element in der 2. Zeile und 1. Spalte des Arrays zu, das ich bereits im vorherigen Code oben erstellt habe:

arr[0, 1]

Wenn Sie nur diese Zeile schreiben, lesen Sie den Wert in dieser Zelle, dh Sie erhalten den Wert in dieser Zelle, aber wenn Sie den gleichen Wert hinzufügen = , schreiben Sie den Wert in diese Zelle, dh legen Sie den Wert in dieser Zelle fest. Sie können die Operatoren + =, - =, * = und / = natürlich auch nur für Zahlen verwenden (int, float, double, __int16, __int32, __int64 usw.), aber Sie wissen es sicher bereits.

Wenn Ihr Projekt keine CLI ist, können Sie die nicht verwaltete Array-Klasse des Standard-Namespace verwenden, wenn Sie dies #include <array>natürlich tun , aber das Problem ist, dass sich diese Array-Klasse vom CLI-Array unterscheidet. Das Erstellen eines Arrays dieses Typs entspricht der CLI, außer dass Sie das ^Vorzeichen und das gcnewSchlüsselwort entfernen müssen . Leider gibt der zweite int-Parameter in den <>Klammern die Länge (dh die Größe) des Arrays an, nicht seinen Rang!

Es gibt keine Möglichkeit, den Rang in dieser Art von Array anzugeben. Der Rang ist nur die Funktion des CLI-Arrays . .

Das std-Array verhält sich wie ein normales Array in c ++, das Sie beispielsweise mit dem Zeiger definieren int*und dann: new int[size]oder ohne Zeiger:, int arr[size]aber im Gegensatz zum normalen Array des c ++ bietet das std-Array Funktionen, die Sie mit den Elementen des Arrays verwenden können. wie Füllen, Beginnen, Beenden, Größe usw., aber normales Array bietet nichts .

Trotzdem sind Standard-Arrays eindimensionale Arrays, wie die normalen C ++ - Arrays. Aber dank der Lösungen, die die anderen Leute vorschlagen, wie Sie das normale eindimensionale C ++ - Array in ein zweidimensionales Array verwandeln können, können wir dieselben Ideen an das Standardarray anpassen, z. B. können wir nach der Idee von Mehrdad Afshari den folgenden Code schreiben:

array<array<int, 3>, 2> array2d = array<array<int, 3>, 2>();

Diese Codezeile erstellt ein "jugged array" , ein eindimensionales Array, das jede seiner Zellen ist oder auf ein anderes eindimensionales Array zeigt.

Wenn alle eindimensionalen Arrays in einem eindimensionalen Array in ihrer Länge / Größe gleich sind, können Sie die Variable array2d als echtes zweidimensionales Array behandeln. Außerdem können Sie die speziellen Methoden zum Behandeln von Zeilen oder Spalten verwenden, je nachdem, wie Sie sie anzeigen Beachten Sie, dass im Standard-Array dieses Standard-Array unterstützt wird.

Sie können auch die Lösung von Kevin Loney verwenden:

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]

Wenn Sie jedoch ein Standardarray verwenden, muss der Code anders aussehen:

array<int, sizeX*sizeY> ary = array<int, sizeX*sizeY>();
ary.at(i*sizeY+j);

Und haben immer noch die einzigartigen Funktionen des Standard-Arrays.

Beachten Sie, dass Sie weiterhin über die []Klammern auf die Elemente des Standardarrays zugreifen können und die atFunktion nicht aufrufen müssen. Sie können auch eine neue int-Variable definieren und zuweisen, die die Gesamtzahl der Elemente im std-Array berechnet und beibehält und deren Wert verwendet, anstatt ihn zu wiederholensizeX*sizeY

Sie können Ihre eigene generische Klasse für zweidimensionale Arrays definieren und den Konstruktor der zweidimensionalen Array-Klasse so definieren, dass zwei Ganzzahlen empfangen werden, um die Anzahl der Zeilen und Spalten im neuen zweidimensionalen Array anzugeben, und die Funktion get abrufen, die zwei Parameter für Ganzzahlen empfängt die auf ein Element im zweidimensionalen Array zugreifen und dessen Wert zurückgeben und eine Funktion festlegen, die drei Parameter empfängt, wobei die beiden ersten Ganzzahlen sind, die die Zeile und Spalte im zweidimensionalen Array angeben, und der dritte Parameter der neue Wert des Element. Der Typ hängt von dem Typ ab, den Sie in der generischen Klasse ausgewählt haben.

Sie können all dies implementieren, indem Sie entweder das normale c ++ - Array (Zeiger oder ohne) oder das std-Array verwenden und eine der Ideen verwenden, die andere vorgeschlagen haben, und die Verwendung wie das cli-Array oder wie die beiden vereinfachen Dimensionsarray, das Sie in C # definieren, zuweisen und verwenden können.


quelle
1

Definieren Sie das Array zunächst mit Zeigern (Zeile 1):

int** a = new int* [x];     //x is the number of rows
for(int i = 0; i < x; i++)
    a[i] = new int[y];     //y is the number of columns
Omar Mohamed Abdel-Sattar
quelle
1

Das folgende Beispiel kann helfen,

int main(void)
{
    double **a2d = new double*[5]; 
    /* initializing Number of rows, in this case 5 rows) */
    for (int i = 0; i < 5; i++)
    {
        a2d[i] = new double[3]; /* initializing Number of columns, in this case 3 columns */
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            a2d[i][j] = 1; /* Assigning value 1 to all elements */
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cout << a2d[i][j] << endl;  /* Printing all elements to verify all elements have been correctly assigned or not */
        }
    }

    for (int i = 0; i < 5; i++)
        delete[] a2d[i];

    delete[] a2d;


    return 0;
}
The_Learner
quelle
1

Wenn Sie ein 2d-Array von Ganzzahlen möchten, deren Elemente nacheinander im Speicher zugewiesen werden, müssen Sie es wie folgt deklarieren

int (*intPtr)[n] = new int[x][n]

Dabei können Sie anstelle von x eine beliebige Dimension schreiben, aber n muss an zwei Stellen gleich sein. Beispiel

int (*intPtr)[8] = new int[75][8];
intPtr[5][5] = 6;
cout<<intPtr[0][45]<<endl;

muss drucken 6.

Vahag Chakhoyan
quelle
0

Ich habe Ihnen eine Lösung hinterlassen, die in bestimmten Fällen für mich am besten funktioniert. Besonders wenn man [die Größe von?] Eine Dimension des Arrays kennt. Sehr nützlich für ein Array von Zeichen, zum Beispiel, wenn wir ein Array mit unterschiedlicher Größe von Zeichenfeldern benötigen [20].

int  size = 1492;
char (*array)[20];

array = new char[size][20];
...
strcpy(array[5], "hola!");
...
delete [] array;

Der Schlüssel sind die Klammern in der Array-Deklaration.

FedeBsAs
quelle
StackOverflow verwendet nur Englisch. Bitte übersetzen Sie Ihre Frage.
M. Mimpen
0

Ich habe dieses nicht elegante, aber SCHNELLE, EINFACHE und ARBEITSSYSTEM verwendet. Ich verstehe nicht, warum dies nicht funktionieren kann, da das System nur dann ein großes Array erstellen und auf Teile zugreifen kann, wenn es nicht in Teile geschnitten wird:

#define DIM 3
#define WORMS 50000 //gusanos

void halla_centros_V000(double CENW[][DIM])
{
    CENW[i][j]=...
    ...
}


int main()
{
    double *CENW_MEM=new double[WORMS*DIM];
    double (*CENW)[DIM];
    CENW=(double (*)[3]) &CENW_MEM[0];
    halla_centros_V000(CENW);
    delete[] CENW_MEM;
}
Mathengineer
quelle
0

Ich weiß nicht genau, ob die folgende Antwort nicht gegeben wurde, aber ich habe beschlossen, der Zuweisung von 2D-Arrays einige lokale Optimierungen hinzuzufügen (z. B. wird eine quadratische Matrix nur durch eine Zuordnung erstellt): int** mat = new int*[n]; mat[0] = new int [n * n];

Das Löschen erfolgt jedoch aufgrund der Linearität der obigen Zuordnung folgendermaßen: delete [] mat[0]; delete [] mat;

sharkzeeh
quelle
Bereits in einer Antwort auf diese Frage erwähnt: stackoverflow.com/a/27672888/103167 und eine Smart-Pointer-Version hier: stackoverflow.com/a/29375830/103167
Ben Voigt vor
-1

Dynamisches Deklarieren von 2D-Arrays:

    #include<iostream>
    using namespace std;
    int main()
    {
        int x = 3, y = 3;

        int **ptr = new int *[x];

        for(int i = 0; i<y; i++)
        {
            ptr[i] = new int[y];
        }
        srand(time(0));

        for(int j = 0; j<x; j++)
        {
            for(int k = 0; k<y; k++)
            {
                int a = rand()%10;
                ptr[j][k] = a;
                cout<<ptr[j][k]<<" ";
            }
            cout<<endl;
        }
    }

Im obigen Code haben wir nun einen Doppelzeiger genommen, ihm einen dynamischen Speicher zugewiesen und einen Wert für die Spalten angegeben. Hier ist der zugewiesene Speicher nur für die Spalten, jetzt für die Zeilen benötigen wir nur eine for-Schleife und weisen dem Wert für jede Zeile einen dynamischen Speicher zu. Jetzt können wir den Zeiger genauso verwenden, wie wir ein 2D-Array verwenden. Im obigen Beispiel haben wir dann unserem 2D-Array (Zeiger) Zufallszahlen zugewiesen. Es dreht sich alles um DMA des 2D-Arrays.

user2808359
quelle
-3

Ich verwende dies beim Erstellen eines dynamischen Arrays. Wenn Sie eine Klasse oder eine Struktur haben. Und das funktioniert. Beispiel:

struct Sprite {
    int x;
};

int main () {
   int num = 50;
   Sprite **spritearray;//a pointer to a pointer to an object from the Sprite class
   spritearray = new Sprite *[num];
   for (int n = 0; n < num; n++) {
       spritearray[n] = new Sprite;
       spritearray->x = n * 3;
  }

   //delete from random position
    for (int n = 0; n < num; n++) {
        if (spritearray[n]->x < 0) {
      delete spritearray[n];
      spritearray[n] = NULL;
        }
    }

   //delete the array
    for (int n = 0; n < num; n++) {
      if (spritearray[n] != NULL){
         delete spritearray[n];
         spritearray[n] = NULL;
      }
    }
    delete []spritearray;
    spritearray = NULL;

   return 0;
  } 
Jedermann
quelle