2Dimensionales dynamisches Array - Pointer zu einem Array - wie von Funktion retournieren?



  • Hallo!

    Ich habe ein 2Dimensionales dynamisches Array.
    Folgender Code in der Funktion:

    char (*cds)[81] = 0; //pointer auf ein array mit 81 chars
    cds = new char [2][81];
    

    Aber wie kann ich diesen Pointer cds nun retournieren? So funktionierts nämlich nicht:

    #include <iostream>
    using namespace std;
    
    char(*)[] erstellen(char (*cds)[81])
    {
      char (*cds)[81] = 0;
      cds = new char [2][81];
      return cds;
    }
    
    int main()
    {
      char (*dvds)[81] = 0;
      dvds = erstellen(dvds);
    
      return 0;
    }
    

    Weiß hier jemand bescheid? Ich wäre sehr sehr dankbar 🙂



  • #include <vector>
    #include <string>
    
    int main()
    {
      std::vector<std::string> > dvds;
    }
    


  • bin schon draufgekommen - falls es jemand braucht ...
    ich retourniere einfach nichts sondern übergebe per referenz an die funktion:

    void erstellen(char (*&cds)[81])
    


  • Falls es jemand brauchen sollte, wäre manni66s Lösung aber besser.



  • Frank92 schrieb:

    void erstellen(char (*&cds)[81])
    

    Willst Du "richtiges" C++ lernen (so wie man es benutzen sollte), oder willst du einfach nur, dass Dein Compiler Dein Programm akzeptiert? Falls ersteres, dann lass Dich doch bitte nochmal stark von der ersten Antwort inspirieren.



  • Und es heißt nicht retournieren.



  • Wenn du unbedingt willst:

    char (*erstellen())[81] { ... }
    


  • kkaw schrieb:

    Willst Du "richtiges" C++ lernen (so wie man es benutzen sollte), oder willst du einfach nur, dass Dein Compiler Dein Programm akzeptiert? Falls ersteres, dann lass Dich doch bitte nochmal stark von der ersten Antwort inspirieren.

    Alles klar vielen Dank! Wir machen in der Lehrveranstaltung nur leider keine Vectoren, deshalb dachte ich, ich darf diese für die Hausaufgabe auch nicht verwenden. Wenn es aber allgemein so gemacht wird, werde ich mir das auf jedsten mal ansehen! 😃



  • Typedefs sind bei solchen Syntaxverrenkungen (das wird mit Funktionszeigern noch unübersichtlicher) ausgesprochen hilfreich. In diesem Fall

    typedef char sinnvoller_name[81];
    
    sinnvoller_name *erstellen() { ... }
    

    Übrigens:

    Frank92 schrieb:

    Wir machen in der Lehrveranstaltung nur leider keine Vectoren, (...)

    Das (und das Hantieren mit nacktem new/delete) kann als Warnsignal gelten. Womöglich bringt man euch C mit Klassen bei statt ordentlichem C++, weil das Skript von (gefühlt) 1995 ist und es seither niemand überarbeitet hat.

    Derartige Lehrveranstaltungen sind so unschön wie häufig. Problematisch ist vor allem, dass man sich mit damit schlechte Angewohnheiten aneignet, die man später mühsam wieder loswerden muss.

    Hier zum Beispiel -- korrigier mich, wenn ich das falsch einschätze -- scheint es so zu sein, dass euch C-Strings (char*) vor C++-Strings (std::string), nacktes new[]/delete[] vor Standard-Containern (std::vector, std::deque und derlei) und nacktes new/delete ohne Erwähnung von Smart-Pointern beigebracht wird, was den unangenehmen Effekt haben kann, dass selbst wenn später die besonders für Anfänger besseren Wege eingeführt werden, sie in den Köpfen der Vorlesungsteilnehmer nicht als Default-Vorgehensweise drinstecken. Stattdessen wenden sie am Ende routinemäßig die Dinge an, die sie zuerst gelernt haben, und das häufig ohne ein ausgeprägtes Verständnis dafür, wie viel Ärger sie sich mit dieser Vorgehensweise einhandeln, weil die Gefahren (insbesondere Exceptions) erst ein halbes Jahr später gegen Vorlesungsende erwähnt wurden.

    Ich erwähne Exceptions ausdrücklich, weil ein großer Anteil der haarraufigen Probleme, die man sich mit der C++-ist-doch-nur-C-mit-mehr-Features-Herangehensweise einhandelt, mit Exceptions und Exceptionsicherheit zusammenhängt. Exceptions sind ein Fehlerbehandlungsmechanismus, der eigentlich in allen Lehrveranstaltungen ganz weit vorne auftauchen sollte, es aber frustrierend selten tut. Grob umrissen: man kann zu beliebigen Zeiten (üblicherweise, wenn eine Ausnahmesituation auftritt, die nicht sofort bereinigt werden kann) eine sog. Exception werfen, die dann den Call-Stack hoch an allem Code vorbeifliegt, der normalerweise ausgeführt würde, bis sich jemand zuständig fühlt und sie behandelt. Das sieht etwa so aus:

    #include <iostream>
    #include <stdexcept>
    
    void inner_function() {
      std::cout << "inner start\n";
      throw std::logic_error("42"); // <-- geworfen werden kann alles mögliche. std::logic_error ist ein Typ
                                    // aus der Standardbibliothek, der zum Melden von Fehlern in der Programmlogik
                                    // vorgesehen ist (die Definition ist leider interpretierbar, aber naja).
                                    // In der Praxis bietet es sich an, von std::logic_error eigene Fehlerklassen
                                    // abzuleiten und die zu benutzen, damit man Fehler leichter auseinanderhalten kann.
                                    // Wenn dir "ableiten" nichts sagt, mach dir keinen Kopf darum, das kriegt ihr noch
                                    // und es ist hierfür erstmal nicht wichtig.
      std::cout << "inner end\n";
    }
    
    void outer_function() {
      std::cout << "outer start\n";
      inner_function();
      std::cout << "outer end\n";
    }
    
    int main() {
      try {
        std::cout << "try start\n";
        outer_function();
        std::cout << "try end\n";
      } catch(std::logic_error const &e) { // <-- fühlt sich für fliegende std::logic_errors zuständig.
        // e.what() ist die Nachricht, die wir dem logic_error oben mitgegeben haben.
        std::cout << e.what() << '\n';
      }
    
      std::cout << "main end\n";
    }
    

    führt zur Ausgabe

    try start
    outer start
    inner start
    42
    main end
    

    Vom Vorbeifliegen ausgenommen (d. h. trotzdem ausgeführt) werden spezielle Funktionen, sog. Destruktoren, die ihr hoffentlich behandeln werdet, wenn ihr Klassen durchnehmt. Für den Moment sei genug, dass an komplexe Datentypen spezielle Aufräumroutinen rangebappt werden können, die automatisch ausgeführt werden, wenn ein Objekt dieses Datentyps seinen Gültigkeitsbereich verlässt, selbst wenn das durch eine fliegende Exception passiert. Diese benutzen std::string, alle Standardcontainer, alle Smart-Pointer und alles andere, was Ressourcen verwaltet, um sicherzustellen, dass Dinge, die in jedem Fall noch getan werden müssen -- meistens das Freigeben der verwalteten Ressource -- auch wirklich getan werden, ohne dass der Programmierer dafür jedes mal komische Klimmzüge machen muss.

    Kurzum, folgenden Umstand solltest du im Hinterkopf behalten:

    void tue_etwas(char *buf, std::size_t buflen) {
      throw std::logic_error("Etwas ist schief gegangen. Das tut uns Leid.");
    }
    
    void falscher_weg() {
      std::size_t n = laenge_bestimmen();
      char *buffer = new char[n];
    
      tue_etwas(buffer, n);
    
      delete[] buffer; // <-- das hier wird nie ausgeführt.
    }
    
    int main() {
      try {
        falscher_weg();
      } catch(std::logic_error const &e) {
        std::cout << e.what() << std::endl;
      }
    }
    

    und

    void tue_etwas(std::string &s) {
      throw std::logic_error("Etwas ist schief gegangen. Das tut uns Leid.");
    }
    
    void richtiger_weg() {
      std::string buffer = irgendeine_initialisierung();
      tue_etwas(buffer);
    } // <-- std::string hat einen Destruktor, der hier aufgerufen wird und 
      // buffer wegräumt. Yay!
    
    int main() {
      try {
        richtiger_weg();
      } catch(std::logic_error const &e) {
        std::cout << e.what() << std::endl;
      }
    }
    

    Dies ist der Hauptgrund, aus dem man C++-Code nicht so schreiben kann wie C-Code, ohne Cthulhu aufzuwecken.



  • seldon schrieb:

    Hier zum Beispiel -- korrigier mich, wenn ich das falsch einschätze -- scheint es so zu sein, dass euch C-Strings (char*) vor C++-Strings (std::string), nacktes new[]/delete[] vor Standard-Containern (std::vector, std::deque und derlei) und nacktes new/delete ohne Erwähnung von Smart-Pointern beigebracht wird, [...]

    Die Lehrveranstaltung heißt zwar "Einführung in C++", aber sogar ein Bekannter von mir hat schonmal gemeint ob ich nicht eher C lerne.
    Und du hast recht, es gibt in der Vorlesung keine C++ Strings und auch keine Vectoren. Es ist sowieso eine sehr merkwürdige Lehrveranstaltung, bei den Tests muss man innerhalb von zwei Minuten rekursive Funktionen auslesen welche char Arrays verändern. Und sobald man einen Buchstaben falsch ausgegeben hat (was bei dem Zeitdruck ja doch vorkommen kann), kann man den Punkt beim Test vergessen. Danach gibt es immer Rätselaufgaben ... aber egal.

    Vielen vielen Dank für die Hilfe! Mir ist mit euren Antworten wirklich sehr geholfen 🙂
    LG Frank92


Anmelden zum Antworten