Feste Größe
1. Übergabe durch Verweis
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 per Referenz zu übergeben, ohne die Dimensionsinformationen zu verlieren, da man sich keine Sorgen machen muss, dass der Aufrufer eine falsche Dimension übergibt (Compilerflags bei Nichtübereinstimmung). Dies ist jedoch bei dynamischen (freestore) Arrays nicht möglich; es funktioniert bei automatischen ( in der Regel stapelfähig ) nur Arrays, d.h. die Dimensionalität sollte zur Kompilierzeit bekannt sein.
2. Passieren durch 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 ist die Übergabe des Arrays per Zeiger. Dies ist nicht zu verwechseln mit der Übergabe durch den zerfallenen Zeigertyp des Arrays (3) Dies ist die gängige und beliebte Methode, die zwar weniger sicher als diese ist, aber dafür flexibler. Wie (1) Verwenden Sie diese Methode, wenn alle Dimensionen des Arrays festgelegt und zur Kompilierungszeit 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, da der Compiler keine Möglichkeit hat, zu prüfen und zu garantieren, dass der Aufrufer die erforderlichen Abmessungen übergibt. Die Funktion verlässt sich nur darauf, was der Aufrufer als Dimension(en) angibt. Diese sind flexibler als die oben genannten, da ihnen immer Arrays unterschiedlicher Länge übergeben werden können.
Es ist zu bedenken, dass es in C nicht möglich ist, ein Array direkt an eine Funktion zu übergeben [während sie in C++ als Referenz übergeben werden können (1) ]; (2) wird ein Zeiger auf das Array und nicht das Array selbst übergeben. Die Übergabe eines Arrays im Ist-Zustand wird zu einer Zeiger-Kopier-Operation, die erleichtert wird durch die Eigenschaft von Arrays, in einen Zeiger zu zerfallen .
3. Übergeben Sie mit (value) einen Zeiger auf den zerfallenen 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 gegenüber der obigen Syntax empfehlen, da die obige Syntax deutlich macht, dass der Bezeichner array
ist ein einzelner Zeiger auf ein Array von 10 Ganzzahlen, während diese Syntax siehe als wäre es ein 2D-Array, aber es ist derselbe Zeiger auf ein Array mit 10 ganzen Zahlen. Hier kennen wir die Anzahl der Elemente in einer einzelnen Zeile (d.h. 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 erkennen 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. Übergabe durch Zeiger an 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;
}
}
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 er in einen Zeiger zerfällt und dadurch zu int **array
. Vielleicht ist es nur ein Hinweis für den Aufrufer, dass das übergebene Array mindestens 10 Spalten haben sollte, selbst dann ist die Zeilenzahl erforderlich. In jedem Fall erkennt der Compiler keine Längen-/Größenverstöße (er prüft nur, ob der übergebene Typ ein Zeiger auf einen Zeiger ist), so dass es hier sinnvoll ist, sowohl die Zeilen- als auch die Spaltenanzahl als Parameter zu verlangen.
Anmerkung: (4) ist die am wenigsten sichere Option da sie kaum eine Typenprüfung hat und am unbequemsten ist. Man kann dieser Funktion nicht rechtmäßig ein 2D-Array übergeben; C-FAQ verurteilt die übliche Umgehung durch int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
da es kann möglicherweise zu undefiniertem Verhalten führen aufgrund der Abflachung des Arrays. Der richtige Weg, ein Array in dieser Methode zu übergeben, bringt uns zu dem unbequemen Teil, d.h. wir brauchen ein zusätzliches (Surrogat-)Array von Zeigern, bei dem jedes Element auf die entsprechende Zeile des eigentlichen, zu übergebenden Arrays zeigt; dieses Surrogat wird dann an die Funktion übergeben (siehe unten); all dies, um dieselbe Aufgabe zu erledigen wie die obigen Methoden, 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**
}