Mehrdimensionale Arrays als Funktionsparameter



  • Warum muss man, wenn man mehrdimensionale Arrays als Funktionsparameter übergibt, die Größe der 2., 3., 4., ... Dimension des Arrays im Array-Operator mitangeben, die der 1. Dimension aber nicht?

    Beispiel-Funktionen:

    //Print eines eindimensionalen Arrays
    void print_array (int my_array[], const int size1) {
    	for (int i = 0; i < size1; i++) {
    		cout << my_array[i] << " ";
    	}
    	cout << endl;
    }
    
    //Print eines zweidimensionalen Arrays, wobei die zweite Dimension die Größe 2 hat: 
    void print_array(int my_array[][2], const int size1, const int size2) {
    	for (int i = 0; i < size1; i++) {
    		for (int j = 0; j < size2; j++) {
    			cout << my_array[i][j] << " ";
    			if (j == size2 - 1) {
    				cout << endl;
    			}
    		}
    	}
    }
    


  • @polleee Damit der Compiler die Position im Speicher berechnen kann.
    Sonst weiß er nicht, wo die nächste Zeile anfängt.

    Im Speicher liegen die Elemente vom Array als ein Block hintereinander.
    Bei C wechselt der rechte Index am schnellsten im Speicher. {a00, a01, a10, a11, a20, a21}

    Wenn man das Array mit my_array[X_SIZE][Y_SIZE] definiert, dann kann man den Offset mit x*Y_SIZE +y berechnen.

    int my_array[X_SIZE][Y_SIZE];
    int *arr = (int*)my_array;
    

    Dann ist arr[x*Y_SIZE +y] dasselbe Element wie my_array[x][y]

    Bei einem 1. dimenionalen Array kann auch der erste Index weg bleiben-


  • Mod

    Bist du in Wirklichkeit in C unterwegs statt in C++? Denn in C++ sollte das besser kein rohes Array sein, sondern ein Objekt, das seine eigenen Dimensionen kennt. Dein Code sieht aus wie C, aber mit cout (von C++) zur Ausgabe, und du fragst im C++-Bereich, daher ist unklar, in welcher Sprache du unterwegs bist.

    Für C(99) gibt es noch die Möglichkeit von VLAs (variable length arrays), die für genau so etwas gut sind:

    #include<stdio.h>
    
    void print2D(int X, int Y, int array[][Y])
    {
      for(int x=0; x < X; ++x)
        {
          for(int y=0; y < Y; ++y)
            printf("%d ", array[x][y]);
          putchar('\n');
        }
    }
    
    int main()
    {
      int array[2][3] = {{1,2,3},{4,5,6}};
      print2D(2, 3, array);
    }
    

    Aber wenn du C++ machst, werden VLAs nicht funktionieren, eben weil man da sowieso bessere Möglichkeiten hätte.

    Für C++ bräuchte man noch Zusatzinfo, ob und welche Dimensionen dynamisch oder fest sein sollen. Wenn sie, wie bei dir, fest sein sollen, dann nimmt man für den Teil besser ein std::array. Ist die äußerste Dimension dynamisch, dann dort ein vector. Ist innen etwas dynamisch, wird's ein bisschen komplizierter (und ist etwas, das in reinem C ganz übel wäre).

    In jedem Fall wird die Funktion und ihr Aufruf schöner, da alle beteiligten Objekte ihre Größe selber kennen. Man braucht sich auf gar nichts festlegen, nicht einmal darauf, dass die "Arrays" irgendwie "rechteckig" wären:

    #include <iostream>
    #include <vector>
    #include <array>
    using namespace std;
    
    template<typename Array2D> void print2D(const Array2D &array_2d)
    {
      for(auto &&outer_it: array_2d )
        {
          for(auto &&inner_it: outer_it)
            cout << inner_it << ' ';
          cout << '\n';
        }
    }
    
    
    
    int main ()
    {
      vector<array<int, 3>> my_array = {{1,2,3}, {4,5,6}};
      vector<vector<int>> weird_thing = {{1, 2}, {3,4,5}, {7}};
      print2D(my_array);
      print2D(weird_thing);
    }
    

    PS: Da hatte ich vor lauter C wohl einen Denkaussetzer. Wieso war das äußere Array bei mir ein Iterator? In der korrigierten Version ist es viel einfacher.



  • Bin in C++ unterwegs und bin Anfänger. Aber ich denke ich habe den Grund verstanden.
    Danke für die schnellen Antworten. 😃


  • Mod

    Tipp: Verschwende weniger Zeit auf C-Spezialitäten (Oder besser noch: Gar keine!). Durch C lernst du kein C++. Lass mich raten: Meinen ersten Code (C) verstehst du so gerade noch (und das obwohl es nicht einmal gültiges C++ ist), aber beim eigentlichen C++-Code (der zweite) verstehst du gar nichts von den ganzen templates, autos, vectoren, und so? Falls ja, machst du C statt C++, denn eigentlich solltest du den zweiten Code vollständig verstehen müssen und nicht den ersten.



  • @SeppJ Da hast du ja auch tiefer in die Trickkiste gegriffen.

    Gibt es einen Grund dafür, dass du für die Range Based Loop eine universal reference verwendest? Da der Funktionsparameter eine "normale" Const Referenz ist, hätte ich die wahrscheinlich auch in der Schleife verwendet:

    template<typename Array2D> void print2D(const Array2D &array_2d)
    {
      for(const auto &outer_it: array_2d )
        {
          for(const auto &inner_it: outer_it)
            cout << inner_it << ' ';
          cout << '\n';
        }
    }
    

    Oder mit Universal Referenz dann so:

    template<typename Array2D> void print2D(Array2D &&array_2d)
    {
      for(auto &&outer_it: array_2d )
        {
          for(auto &&inner_it: outer_it)
            cout << inner_it << ' ';
          cout << '\n';
        }
    }
    

  • Mod

    Nein, das hat keinen tieferen Sinn sondern ist ein Überbleibsel aus der Bearbeitung des Quellcodes hier im Forum über mehrere Iterationen hinweg. Da ist mir eine Änderung entflutscht. Eine deiner Varianten meinte ich, egal welche, Hauptsache konsistent.

    In beiden Fällen würde ich das nicht als so trickreich ansehen. Als C++-Lerner muss man so etwas lesen können und anstreben, in dieser Art zu programmieren. Hier sieht man wunderbar die Macht der generischen Programmierung: Der Algorithmus ist identisch zur Variante in C, aber die Funktion kann so viel mehr, sowohl was die Art der Parameter angeht, als auch die Form der dargestellten Objekte.



  • Mit Trickreich meine ich vor allem && Denn Universal Referenz vs. rvalue Referenz vs normale Referenz verlangt zumindest von mir noch immer, dass ich aktiv darüber nachdenke. Da kann ich mir gut vorstellen, dass das Anfänger noch nie gesehen haben.


  • Mod

    Du überschätzt, wie viel C++ ich überhaupt noch programmiere, und wie viel ich darüber nachdenke 🙂
    Das && habe ich nicht aus tieferen Überlegungen gesetzt, sondern weil ich, als ich noch mehr C++ gemacht habe, mir mal grob gemerkt habe, dass das bei templates und auto tendenziell besser ist.


Anmelden zum Antworten