Ich habe eine Funktion, die ich als Parameter nehmen möchte, ein 2D-Array variabler Größe.
Bisher habe ich das:
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];
Allerdings anrufen myFunction(anArray)
gibt mir einen Fehler.
Ich möchte das Array nicht kopieren, wenn ich es übergebe. Alle Änderungen, die in vorgenommen wurden myFunction
sollte den Zustand ändern anArray
. Wenn ich das richtig verstehe, möchte ich als Argument nur einen Zeiger auf ein 2D-Array übergeben. Die Funktion muss auch Arrays unterschiedlicher Größe akzeptieren. Also zum Beispiel [10][10]
und [5][5]
. Wie kann ich das machen?
Es gibt drei Möglichkeiten, ein 2D-Array an eine Funktion zu übergeben:
-
Der Parameter ist ein 2D-Array
int array[10][10];
void passFunc(int a[][10])
{
// ...
}
passFunc(array);
-
Der Parameter ist ein Array, das Zeiger enthält
int *array[10];
for(int i = 0; i < 10; i++)
array[i] = new int[10];
void passFunc(int *a[10]) //Array containing pointers
{
// ...
}
passFunc(array);
-
Der Parameter ist ein Zeiger auf einen Zeiger
int **array;
array = new int *[10];
for(int i = 0; i <10; i++)
array[i] = new int[10];
void passFunc(int **a)
{
// ...
}
passFunc(array);
Feste Größe
1. Als Referenz übergeben
template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
In C++ ist es wahrscheinlich am sichersten, das Array als Referenz zu übergeben, ohne die Dimensionsinformationen zu verlieren, 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 Stack-lebende) Arrays, dh die Dimensionalität sollte zur Kompilierzeit bekannt sein.
2. Zeiger übergeben
void process_2d_array_pointer(int (*array)[5][10])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < 5; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << (*array)[i][j] << '\t';
std::cout << std::endl;
}
}
Das C-Äquivalent der vorherigen Methode übergibt das Array per Zeiger. Dies sollte nicht mit dem Übergeben des verfallenen Zeigertyps des Arrays verwechselt werden (3), das ist die übliche, beliebte Methode, wenn auch weniger sicher als diese, aber flexibler. Wie (1), verwenden Sie diese Methode, wenn alle Dimensionen des Arrays fest und zur Kompilierzeit bekannt sind. Beachten Sie, dass beim Aufruf der Funktion die Adresse des Arrays übergeben werden sollte process_2d_array_pointer(&a)
und nicht die Adresse des ersten Elements durch Zerfall process_2d_array_pointer(a)
.
Variable Größe
Diese werden von C geerbt, sind aber weniger sicher, der Compiler hat keine Möglichkeit zu überprüfen, ob der Aufrufer die erforderlichen Dimensionen übergibt. Die Funktion stützt sich nur darauf, was der Aufrufer als Dimension(en) übergibt. Diese sind flexibler als die oben genannten, da ihnen immer Arrays unterschiedlicher Länge übergeben werden können.
Es sei daran erinnert, dass es in C nicht möglich ist, ein Array direkt an eine Funktion zu übergeben [while in C++ they can be passed as a reference (1)]; (2) übergibt einen Zeiger auf das Array und nicht das Array selbst. Ein Array immer so zu übergeben, wie es ist, wird zu einer Pointer-Copy-Operation, die durch die Natur des Arrays erleichtert wird, in einen Pointer zu zerfallen.
3. Übergeben (Wert) einen Zeiger auf den verfallenen Typ
// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Obwohl int array[][10]
erlaubt ist, würde ich es nicht über die obige Syntax empfehlen, da die obige Syntax klar macht, dass der Bezeichner array
ist ein einzelner Zeiger auf ein Array von 10 ganzen Zahlen, während diese Syntax sieht aus als wäre es ein 2D-Array, ist aber derselbe Zeiger auf ein Array mit 10 Ganzzahlen. 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 gibt es eine gewisse Sicherheit, da der Compiler kennzeichnen 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. Siehe hier für die Begründung, warum nur die erste Dimension weggelassen werden darf.
4. Zeiger an einen Zeiger übergeben
// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Auch hier gibt es eine alternative Syntax von int *array[10]
was dasselbe ist wie int **array
. In dieser Syntax ist die [10]
wird ignoriert, da es in einen Zeiger zerfällt, wodurch es wird int **array
. Vielleicht ist es nur ein Hinweis für den Aufrufer, dass das übergebene Array mindestens 10 Spalten haben sollte, selbst dann ist eine Zeilenanzahl erforderlich. In jedem Fall markiert der Compiler keine Längen-/Größenverletzungen (er prüft nur, ob der übergebene Typ ein Zeiger auf einen Zeiger ist), daher ist es hier sinnvoll, sowohl die Zeilen- als auch die Spaltenanzahl als Parameter zu erfordern.
Notiz: (4) ist die am wenigsten sichere Option da es kaum typprüfung hat und am unpraktischsten ist. Man kann dieser Funktion kein 2D-Array rechtmäßig übergeben; C-FAQ verurteilt die übliche Problemumgehung zu tun int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
da dies aufgrund von Array-Flattening möglicherweise zu undefiniertem Verhalten führen kann. Die richtige Art, ein Array in dieser Methode zu übergeben, bringt uns zum unbequemen Teil, dh wir brauchen ein zusätzliches (Ersatz-) Array von Zeigern, wobei jedes seiner Elemente auf die entsprechende Zeile des tatsächlichen, zu übergebenden Arrays zeigt; dieses Surrogat 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:
#include <iostream>
// copy above functions here
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array
process_2d_array(a, 5);
// works since a's first dimension decays into a pointer thereby becoming int (*)[10]
int *b[5]; // surrogate
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
// another popular way to define b: here the 2D arrays dims may be non-const, runtime var
// int **b = new int*[5];
// for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
process_pointer_2_pointer(b, 5, 10);
// process_2d_array(b, 5);
// doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
Als Modifikation des ersten Vorschlags von Shengy können Sie Vorlagen verwenden, um die Funktion dazu zu bringen, eine mehrdimensionale Array-Variable zu akzeptieren (anstatt ein Array von Zeigern zu speichern, die verwaltet und gelöscht werden müssen):
template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
printf("%p\n", &arr);
}
int main()
{
double a1[10][10];
double a2[5][5];
printf("%p\n%p\n\n", &a1, &a2);
func(a1);
func(a2);
return 0;
}
Die print-Anweisungen zeigen, dass die Arrays als Referenz übergeben werden (durch Anzeigen der Variablenadressen).
Überrascht, dass dies bisher noch niemand erwähnt hat, aber Sie können einfach alles als Vorlage verwenden, das 2D unterstützt [][] Semantik.
template <typename TwoD>
void myFunction(TwoD& myArray){
myArray[x][y] = 5;
etc...
}
// call with
double anArray[10][10];
myFunction(anArray);
Es funktioniert mit jeder “Array-ähnlichen” 2D-Datenstruktur, wie z std::vector<std::vector<T>>
oder ein benutzerdefinierter Typ, um die Wiederverwendung von Code zu maximieren.
Sie können eine Funktionsvorlage wie folgt erstellen:
template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
myArray[x][y] = 5;
etc...
}
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önnten es jedoch als Wrapper für eine Funktion wie diese verwenden:
void myFunction(double * arr, int R, int C)
{
arr[x * C + y] = 5;
etc...
}
Es behandelt das Array als eindimensional und verwendet Arithmetik, um die Offsets der Indizes herauszufinden. In diesem Fall würden Sie die Vorlage wie folgt definieren:
template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
myFunction(*myArray, R, C);
}
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, die der Compiler zu adressieren weiß, weil Sie die Dimensionen angegeben haben. Sie müssen es als Array an eine Funktion übergeben. Sie können die Größe der Anfangsdimension wie folgt weglassen:
void f(double p[][10]) {
}
Dadurch können Sie jedoch keine Arrays mit einer anderen letzten Dimension als zehn übergeben.
Die beste Lösung in C++ ist die Verwendung std::vector<std::vector<double> >
: Es ist fast genauso effizient und deutlich komfortabler.
Ein eindimensionales Array zerfällt zu einem Zeigerzeiger, 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 –
void myFunction(double (*myArray) [10]);
ich würde bevorzugen std::vector
über rohe Arrays.
kann Parameter 3 nicht von ‘double [10][10]’ verdoppeln **’
– RogerDarwin
7. Januar 2012 um 3:43 Uhr
Die akzeptierte Antwort zeigt nur 2 Techniken [its (2) and (3) are the same] aber es gibt 4 einzigartige Möglichkeiten, ein 2D-Array an eine Funktion zu übergeben.
– legends2k
24. März 2014 um 11:44 Uhr
Genau genommen, ja, sie sind keine 2D-Arrays, aber diese Konvention (obwohl sie zu UB führt), ein Array von Zeigern zu haben, die jeweils auf (ein 1D-) Array zeigen, scheint weit verbreitet zu sein 🙁 Ein abgeflachtes 1D-Array von mxn haben Länge, mit Hilfsfunktionen/Klasse, um ein 2D-Array zu emulieren, ist vielleicht besser.
– legends2k
12. Juli 2015 um 8:40 Uhr
AM EINFACHSTEN–
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)); }
. Nenn es wie-int mat[3][5]; func(mat[0], 3, 5);
– Minhas Kamal
9. Januar 2019 um 4:24 Uhr