2D array per reference an eine funktion übergeben?

4 Antworten

Vom Beitragsersteller als hilfreich ausgezeichnet

Eigentlich steht alles schon im Link von regex9's Antwort, aber es ist im einfachsten, mehrdimensionale Arrays als Pointer auf das erste Element zu übergeben, und danach die Größe jeder einzelnen Dimension.

Vorweg: Das, was du vor hast geht nicht, da es keine Refenzen auf mehrdimensionale Arrays mit mindestens einer unbekannten Dimension geben kann. Für Referenzen müssen alle Größen bekannt sein. Ansonsten gehen natürlich Referenzen auf Zeiger, oder Referenzen auf Zeiger auf Zeiger auf Zeiger auf Zeiger usw., aber das wäre etwas sinnlos. Dazu später mehr ...

Wenn es wirklich eine Referenz sein muss, dann am besten nur, wenn dem Compiler im aktuellen Gültigkeitsbereich auch die Dimensionsgrößen bekannt sind. (Der Punkt ist enorm wichtig!)

Das geht am übersichtlichsten mit Templates, bläht aber den Code etwas auf, wenn du eine Million unterschiedlicher Größenkombinationen hast.

Beide Möglichkeiten sind in folgendem Code zusammen gefasst (Beachte dabei bitte, WIE auf die einzelnen Arrayelemente innerhalb der jeweils innerersten Schleife zugegriffen wird und wie die Argumente an die beiden Funktionen am Ende von Main übergeben werden!):

#include <iostream> // cout, endl
#include <string>

#include <cstdlib> // size_t

void dump_table(
const ::std::string * const table,
const size_t rows, const size_t cols
) {
using namespace ::std; // cout, endl

cout << "PTR";
cout << "[" << rows << "][" << cols << "]" << endl;

for (size_t row = 0; row < rows; ++row) {
cout << '#' << row << ':';

for (size_t col = 0; col < cols; ++col) {
cout << ' ' << *(table + row * rows + col);
}

cout << endl;
}
}

template <size_t ROWS, size_t COLS>
void dump_table(
const ::std::string (& table)[ROWS][COLS]
) {
using namespace ::std; // cout, endl

cout << "REF";
cout << "[" << ROWS << "][" << COLS << "]" << endl;

for (size_t row = 0; row < ROWS; ++row) {
cout << '#' << row << ':';

for (size_t col = 0; col < COLS; ++col) {
cout << ' ' << table[row][col];
}

cout << endl;
}
}

int main(void) {
constexpr size_t rows = 3;
constexpr size_t cols = 4;

const ::std::string table [rows][cols] {
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"}
};

dump_table(table); // ref
dump_table(&table[0][0], rows, cols); // ptr
}

Das ist nicht das, was du gefragt hast, aber wenn du WIRKLICH ein zweidimensionales Array mit unbekannter Größe mindestens einer Dimension per Referenz übergeben willst / musst / sollst, dann geht das nicht. Du benötigst für alle Dimensionen eine Größeninformation (zur Kompilierzeit!).

Ich weiß zwar nicht, warum man so ein Knickei haben wollen würde, aber so würde es als Pointer-Variante mit unbekannter erster Dimension aussehen:

template <size_t COLS>
void dump_table(
const ::std::string (* const table)[COLS],
const size_t rows
) {
using namespace ::std; // cout, endl

cout << "UGLY";
cout << "[" << rows << "][" << COLS << "]" << endl;

for (size_t row = 0; row < rows; ++row) {
cout << '#' << row << ':';

for (size_t col = 0; col < COLS; ++col) {
cout << ' ' << table[row][col];
}

cout << endl;
}
}

Der Aufruf in main() sähe dann - an den Code aus dem ersten Code-Schnipsel angelehnt - so aus:

dump_table(table, rows); // ugly

Also quasi eine unschöne Mischung aus den ersten beiden vernünftigen Varianten. Würde ich so aber nicht machen wollen.

Merk dir einfach:

Du KANNST keine Referenz auf ein mehrdimensionales Array mit mindestens einer unbekannten Dimension definieren! Sind hingegen alle Dimensionsgrößen bekannt, geht das komfortabel mit einem Template oder zur Not mit magischen Werten:

void dump_table(const ::std::string (& table)[3][4]);

Ein Template ist aber ordentlicher, sicherer und zuverlässiger, vor allem, wenn der Code später mal geändert wird.

Fazit: So wie in dem Beispiel aus deiner Frage, geht es nicht als Referenz, da die Größe der ersten Dimension unbekannt ist!

Schönen Tag noch! :)

TeeTier  04.11.2017, 08:02

Nachtrag: Mir fällt gerade ein, dass ich mal bei den CppCoreGuidelines eine Hilfsklasse gesehen habe, deren Name mir jetzt zwar entfallen ist, aber die dafür sorgte, dass man Arrays an Funktionen übergeben konnte, ohne Größeninformationen zu verlieren, und ohne auf zusätzliche Argumente angewiesen zu sein.

Habe hier mal kurz so etwas für ein 2-dimensionales Array geschrieben und muss sagen, dass das vermutlich die sauberste Lösung von allen ist:

#include <iostream> // cout, endl
#include <string>

#include <cstdlib> // size_t

template <typename T>
struct Array2D {

template <size_t ROWS, size_t COLS>
constexpr Array2D(const T (& arr)[ROWS][COLS]) noexcept :
ptr { &arr[0][0] },
rows { ROWS }, cols { COLS }
{}

Array2D(const Array2D &) = delete;
Array2D(Array2D &&) = delete;

Array2D & operator = (const Array2D &) = delete;
Array2D & operator = (Array2D &&) = delete;

~Array2D() = default;

const T & at(size_t row, size_t col) const noexcept {
return *(ptr + row * rows + col);
}

const T * ptr;

const size_t rows;
const size_t cols;

};

void dump_table(
const Array2D<::std::string> & table
) {
using namespace ::std; // cout, endl

cout << "WRAPPED";
cout << "[" << table.rows << "]";
cout << "[" << table.cols << "]";
cout << endl;

for (size_t row = 0; row < table.rows; ++row) {
cout << '#' << row << ':';

for (size_t col = 0; col < table.cols; ++col) {
cout << ' ' << table.at(row, col);
}

cout << endl;
}
}

int main(void) {
constexpr size_t rows = 3;
constexpr size_t cols = 4;

const ::std::string table [rows][cols] {
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"}
};

dump_table(table); // wrapped
}

Hier wird kein Template der Funktion "dump_table()" benötigt, und das Hilfs-Template-Objekt Array2D wird ohne Overhead direkt als Argument erzeugt.

Das Ganze käme einer Referenz auf ein 2D-Array am nächsten, und da Größeninfos direkt ans Objekt gebunden sind, ist es auch noch relativ sicher / komfortabel. Kann natürlich noch ausgebaut werden, aber so ungefähr stelle ich mir das Optimum vor. :)

0

include <vector> anschauen falls die Anwendung nicht Zeitkritisch ist.