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 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
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 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 Zerfall process_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
// 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 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 Bezeichner array
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
// 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;
}
}
Wieder gibt es eine alternative Syntax int *array[10]
, die dieselbe ist wie int **array
. In dieser Syntax wird das [10]
ignoriert, wenn 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, 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:
#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**
}