Ich habe eine Funktion, die ich als Parameter für ein 2D-Array mit variabler Größe verwenden möchte.
Bisher habe ich Folgendes:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
Und ich habe an anderer Stelle in meinem Code ein Array deklariert:
double anArray[10][10];
Ein Anruf myFunction(anArray)
gibt mir jedoch einen Fehler.
Ich möchte das Array nicht kopieren, wenn ich es übergebe. Alle vorgenommenen Änderungen myFunction
sollten den Status von ändern anArray
. Wenn ich das richtig verstehe, möchte ich nur einen Zeiger auf ein 2D-Array als Argument übergeben. Die Funktion muss auch Arrays unterschiedlicher Größe akzeptieren. So zum Beispiel [10][10]
und [5][5]
. Wie kann ich das machen?
c++
arrays
pointers
multidimensional-array
RogerDarwin
quelle
quelle
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Nennen wir es wieint mat[3][5]; func(mat[0], 3, 5);
Antworten:
Es gibt drei Möglichkeiten, ein 2D-Array an eine Funktion zu übergeben:
Der Parameter ist ein 2D-Array
Der Parameter ist ein Array, das Zeiger enthält
Der Parameter ist ein Zeiger auf einen Zeiger
quelle
array
mit bekommenarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
. h .Feste Größe
1. Als Referenz übergeben
In C ++ ist das Übergeben des Arrays als Referenz ohne Verlust der Dimensionsinformationen wahrscheinlich das sicherste, da man sich keine Sorgen machen muss, dass der Aufrufer eine falsche Dimension übergibt (Compiler-Flags bei Nichtübereinstimmung). Dies ist jedoch mit dynamischen (Freestore-) Arrays nicht möglich. Es funktioniert nur für automatische ( normalerweise stapelweise lebende ) Arrays, dh die Dimensionalität sollte zur Kompilierungszeit bekannt sein.
2. Übergeben Sie den Zeiger
Das C-Äquivalent der vorherigen Methode übergibt das Array per Zeiger. Dies sollte nicht mit der Übergabe des verfallenen Zeigertyps des Arrays verwechselt werden (3) ist die übliche, beliebte Methode, wenn auch weniger sicher als diese, aber flexibler. Verwenden Sie diese Methode wie in (1) , wenn alle Dimensionen des Arrays zur Kompilierungszeit festgelegt und bekannt sind. Beachten Sie, dass beim Aufrufen der Funktion die Adresse des Arrays übergeben werden sollte
process_2d_array_pointer(&a)
und nicht die Adresse des ersten Elements durch Zerfallprocess_2d_array_pointer(a)
.Variable Größe
Diese werden von C geerbt, sind jedoch weniger sicher. Der Compiler kann dies nicht überprüfen und garantiert, dass der Aufrufer die erforderlichen Dimensionen übergibt. Die Funktion basiert nur auf dem, was der Anrufer als Dimension (en) übergibt. Diese sind flexibler als die oben genannten, da Arrays unterschiedlicher Länge immer an sie übergeben werden können.
Es ist zu beachten, dass es in C kein direktes Übergeben eines Arrays an eine Funktion gibt [während sie in C ++ als Referenz übergeben werden können (1) ]; (2) übergibt einen Zeiger auf das Array und nicht auf das Array selbst. Das Übergeben eines Arrays im Ist-Zustand wird zu einer Zeigerkopieroperation, die durch die Art des Zerfalls des Arrays in einen Zeiger erleichtert wird .
3. Übergeben Sie (Wert) einen Zeiger auf den verfallenen Typ
Obwohl dies
int array[][10]
zulässig ist, würde ich es nicht über die obige Syntax empfehlen, da die obige Syntax deutlich macht, dass der Bezeichnerarray
ein einzelner Zeiger auf ein Array mit 10 Ganzzahlen ist, während diese Syntax wie ein 2D-Array aussieht, aber der gleiche Zeiger auf ist ein Array von 10 ganzen Zahlen. Hier kennen wir die Anzahl der Elemente in einer einzelnen Zeile (dh die Spaltengröße, hier 10), aber die Anzahl der Zeilen ist unbekannt und muss daher als Argument übergeben werden. In diesem Fall besteht eine gewisse Sicherheit, da der Compiler markieren kann, wenn ein Zeiger auf ein Array mit einer zweiten Dimension ungleich 10 übergeben wird. Die erste Dimension ist der variierende Teil und kann weggelassen werden. Hier finden Sie die Gründe, warum nur die erste Dimension weggelassen werden darf.4. Übergeben Sie den Zeiger auf einen Zeiger
Wieder gibt es eine alternative Syntax
int *array[10]
, die dieselbe ist wieint **array
. In dieser Syntax wird das[10]
ignoriert, wenn es in einen Zeiger zerfällt, wodurch es wirdint **array
. Vielleicht ist es nur ein Hinweis für den Aufrufer, dass das übergebene Array mindestens 10 Spalten haben sollte, auch wenn eine Zeilenanzahl erforderlich ist. In jedem Fall kennzeichnet der Compiler keine Längen- / Größenverletzungen (er prüft nur, ob der übergebene Typ ein Zeiger auf Zeiger ist), weshalb hier sowohl Zeilen- als auch Spaltenanzahl als Parameter erforderlich sind.Hinweis: (4) ist die am wenigsten sichere Option, da kaum eine Typprüfung durchgeführt wird und die unpraktischste ist. Ein 2D-Array kann dieser Funktion nicht legitim übergeben werden. C-FAQ verurteilt die übliche Problemumgehung,
int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
da dies möglicherweise zu undefiniertem Verhalten aufgrund von Array-Abflachung führen kann. Die richtige Art, ein Array in dieser Methode zu übergeben, bringt uns zu dem unbequemen Teil, dh wir benötigen ein zusätzliches (Ersatz-) Array von Zeigern, wobei jedes seiner Elemente auf die jeweilige Zeile des tatsächlich zu übergebenden Arrays zeigt. Dieser Ersatz wird dann an die Funktion übergeben (siehe unten). All dies, um die gleiche Arbeit wie die oben genannten Methoden zu erledigen, die sicherer, sauberer und vielleicht schneller sind.Hier ist ein Treiberprogramm zum Testen der oben genannten Funktionen:
quelle
b[i] = a[i];
, sagen wir,b[i] = new int[10];
. Man kann auchb
dynamisch zuweisenint **b = int *[5];
und es wird immer noch so funktionieren, wie es ist.array[i][j]
in der Funktion in 4) ? Weil es ptr zu ptr empfangen hat und den Wert der letzten Dimension nicht kennt, was ist notwendig, um eine Verschiebung für die korrekte Adressierung durchzuführen?array[i][j]
ist nur eine Zeigerarithmetik, dh zum Wert des Zeigersarray
, würde esi
das Ergebnis addieren und dereferenzieren alsint*
, zu dem esj
diesen Ort addieren und dereferenzieren würde , indem es einen liestint
. Also, nein, es muss keine Dimension dafür kennen. Aber das ist der springende Punkt! Der Compiler nimmt das Wort des Programmierers im Glauben und wenn der Programmierer falsch war, kommt es zu undefiniertem Verhalten. Dies ist der Grund, warum ich erwähnt hatte, dass Fall 4 die am wenigsten sichere Option ist.Als Modifikation von Shengys erstem Vorschlag können Sie Vorlagen verwenden, damit die Funktion eine mehrdimensionale Arrayvariable akzeptiert (anstatt ein Array von Zeigern zu speichern, die verwaltet und gelöscht werden müssen):
Die print-Anweisungen sollen zeigen, dass die Arrays als Referenz übergeben werden (indem die Adressen der Variablen angezeigt werden).
quelle
%p
einen Zeiger drucken, und selbst dann müssen Sie ihn umwandelnvoid *
, sonst wirdprintf()
undefiniertes Verhalten aufgerufen. Darüber hinaus sollten Sie&
beim Aufrufen der Funktionen nicht den Operator addressof ( ) verwenden, da die Funktionen ein Argument vom Typ erwartendouble (*)[size_y]
, während Sie sie derzeitdouble (*)[10][10]
und übergebendouble (*)[5][5]
.Überrascht, dass dies noch niemand erwähnt hat, aber Sie können einfach eine 2D-Vorlage für [] [] -Semantik erstellen.
Es funktioniert mit jeder "Array-ähnlichen" 2D-Datenstruktur, z. B.
std::vector<std::vector<T>>
oder einem benutzerdefinierten Typ, um die Wiederverwendung von Code zu maximieren.quelle
Sie können eine Funktionsvorlage wie folgt erstellen:
Dann haben Sie beide Dimensionsgrößen über R und C. Für jede Arraygröße wird eine andere Funktion erstellt. Wenn Ihre Funktion also groß ist und Sie sie mit einer Vielzahl unterschiedlicher Arraygrößen aufrufen, kann dies kostspielig sein. Sie können es jedoch als Wrapper für eine Funktion wie diese verwenden:
Es behandelt das Array als eindimensional und verwendet Arithmetik, um die Offsets der Indizes zu ermitteln. In diesem Fall würden Sie die Vorlage folgendermaßen definieren:
quelle
size_t
ist der bessere Typ für Array-Indizes alsint
.anArray[10][10]
ist kein Zeiger auf einen Zeiger, sondern ein zusammenhängender Speicherblock, der zum Speichern von 100 Werten vom Typ double geeignet ist. Der Compiler kann diese adressieren, da Sie die Dimensionen angegeben haben. Sie müssen es an eine Funktion als Array übergeben. Sie können die Größe der anfänglichen Dimension wie folgt weglassen:Auf diese Weise können Sie jedoch keine Arrays mit der letzten Dimension außer zehn übergeben.
Die beste Lösung in C ++ ist die Verwendung
std::vector<std::vector<double> >
: Sie ist fast genauso effizient und wesentlich bequemer.quelle
Ein eindimensionales Array zerfällt in einen Zeiger, der auf das erste Element im Array zeigt. Während ein 2D-Array zu einem Zeiger zerfällt, der auf die erste Zeile zeigt. Der Funktionsprototyp sollte also sein -
Ich würde
std::vector
rohen Arrays vorziehen .quelle
Sie können so etwas tun ...
Ihre Ausgabe wird wie folgt sein ...
quelle
Hier ist ein Beispiel für eine Vektormatrix
Ausgabe:
quelle
Wir können ein 2D-Array auf verschiedene Arten an eine Funktion übergeben:
Mit einem einzelnen Zeiger müssen wir das 2D-Array typisieren.
Verwenden des Doppelzeigers Auf diese Weise wird auch das 2d-Array typisiert
quelle
Eine wichtige Sache für die Übergabe mehrdimensionaler Arrays ist:
First array dimension
muss nicht angegeben werden.Second(any any further)dimension
muss angegeben werden.1.Wenn nur die zweite Dimension global verfügbar ist (entweder als Makro oder als globale Konstante)
2.Verwenden eines einzelnen Zeigers : Bei dieser Methode müssen wir das 2D-Array bei der Übergabe an die Funktion typisieren .
quelle
Sie können dazu die Vorlagenfunktion in C ++ verwenden. Ich habe so etwas gemacht:
Das Problem bei diesem Ansatz besteht darin, dass für jeden von Ihnen angegebenen Wert von col die neue Funktionsdefinition mithilfe der Vorlage instanziiert wird. damit,
Instanziiert die Vorlage zweimal, um zwei Funktionsdefinitionen zu erstellen (eine mit col = 3 und eine mit col = 5).
quelle
Wenn Sie weitergeben wollen
int a[2][3]
zuvoid func(int** pp)
müssen Sie Hilfsschritte wie folgt.Da die erste
[2]
implizit angegeben werden kann, kann sie weiter vereinfacht werden als.quelle
Wenn Sie ein dynamisches 2D-Array an eine Funktion übergeben möchten, kann die Verwendung einiger Zeiger für Sie hilfreich sein.
quelle
Sie dürfen die Dimension ganz links weglassen und haben zwei Möglichkeiten:
Dies gilt auch für Zeiger:
Der Zerfall eines N-dimensionalen Arrays in einen Zeiger auf ein N-1-dimensionales Array ist nach dem C ++ - Standard zulässig , da Sie die am weitesten links stehende Dimension verlieren und dennoch korrekt auf Array-Elemente mit N-1-Dimensionsinformationen zugreifen können.
Details hier
Obwohl, Arrays und Zeiger sind nicht die gleichen : eine Anordnung in einen Zeiger des Zerfalls kann, sondern ein Zeiger nicht trägt Zustand über die Größe / Konfiguration der Daten , auf die sie verweist.
A
char **
ist ein Zeiger auf einen Speicherblock, der Zeichenzeiger enthält , die selbst auf Speicherblöcke von Zeichen zeigen. Achar [][]
ist ein einzelner Speicherblock, der Zeichen enthält. Dies hat Auswirkungen darauf, wie der Compiler den Code übersetzt und wie die endgültige Leistung sein wird.Quelle
quelle