eigener Stack-Versuch


  • Mod

    Seikuassi schrieb:

    Lauter Fehler, viele davon hätte dir auch dein Compiler nennen können, wenn du nur Warnungen eingeschaltet hättest.

    😕 LAUTER Fehler? Das size war falsch, aber sonst?

    Vor allem Designfehler. Ansonsten noch direkte Compilerwarnungen: Einige deiner Vergleiche sind immer true/false.

    Außerdem hat mir der Compiler keine Warnungen (und natürlich auch Fehler) angezeigt 😃 .

    Dann schalt sie ein!

    Was meinst du mit Design? Ist das ein schlechtes Design bzw. wie hättest es du gemacht?

    Absolute Todsünden, Tabu, geht gar nicht, sofort ändern:
    -Ressourcenverwaltung ohne RAII
    -Regel der großen Drei nicht beachtet. Nun ja, eigentlich hast du sie schon beachtet, da du weder Konstruktor, Destruktor, noch Zuweisungsoperator hast. ABER:
    -Du hast weder Konstruktor noch Destruktor, obwohl du zwei Methoden initialize und destroy hast!

    Microcontroller sind keine Ausrede für keinen dieser Punkte. Dies sind Kernvorteile von C++, die du hier verschenkst. Dies sind die Gründe für C++ auf dem Microcontroller! edit: Da du den Microcontroller in Assembler programmierst, nehme ich mal an, dies ist für normale PCs. Da gilt dies natürlich auch.)

    Kleinere Sünden:
    -Fehlerbehandlung in der Klasse. Das schreit nach Exceptions. Hier kann ich eventuell verstehen, dass man das auf dem Microcontroller eventuell nicht so macht. Aber bestimmt auch nicht so wie du!
    -Warum überhaupt den Stack im vorhinein begrenzen? Du benutzt ohnehin dynamische Speicherverwaltung, wieso nicht dynamisch?
    -Warum verwaltet der Stack überhaupt selber Speicher? Der Stack sollte eine Schnittstelle für das push und pop sein, die Speicherverwaltung soll er ruhig jemandem überlassen, der sich mit so etwas auskennt. Zum Beispiel einem std::vector, einer deque oder list. Oder meinetwegen auch einer Eigenkreation. Hauptsache klare Trennung der Aufgabenbereiche!
    -Das ganze Verhalten bezüglich underflow und Anzahl und Position der Elemente kommt mir unnötig umständlich vor. Ich bin mir intuitiv recht sicher, dass das wesentlich einfacher ginge.

    Falsch verstandene Aspekte der Sprache:
    -Bist du sicher, dass 68-72 das machen, was du denkst?
    -Nullzeiger und wie sie sich mit delete verhalten
    -peek()

    Folgender Code funktioniert:

    Reiner Zufall, der Fehler ist immer noch drin. Du hast ihn gar nicht korrigiert!



  • Die Syntax von if-else erfordert übrigens nur ein Klammernpaar bei der Bedingung. Ich will gar nicht spekulieren, warum du da zwei Klammernpaare hinschreibst, da fallen mir nur böse Sachen ein.


  • Mod

    Bashar schrieb:

    Die Syntax von if-else erfordert übrigens nur ein Klammernpaar bei der Bedingung. Ich will gar nicht spekulieren, warum du da zwei Klammernpaare hinschreibst, da fallen mir nur böse Sachen ein.

    Das stellt diese doofen Warnungen ab, falls man versehentlich einmal den Zuweisungsoperator anstatt eines Vergleichs benutzt 🕶 .



  • SeppJ schrieb:

    Das stellt diese doofen Warnungen ab, falls man versehentlich einmal den Zuweisungsoperator anstatt eines Vergleichs benutzt 🕶 .

    Ich hab diese Warnung ja immer explizit deaktiviert. Ich wuerde gerne behaupten, dass wenn ich = schreibe, ich das auch meine, aber letztens habe ich um 4:30 Morgens tatsaechlich = statt == geschrieben und 1h lang gesucht 😞



  • Hallo nochmal,

    Ups, ja natürlich muss der Konstruktor und Destruktor vorhanden sein (dadurch kann das blöde initialize () weg).

    Den Destruktor, habe ich in sofern nicht verstanden, da ich nicht weiß, wie man diesen aufruft 🙄 . Kann mir jemand einen Beispielcode für z.B. den deque oder vector geben?

    SeppJ schrieb:

    Ressourcenverwaltung ohne RAII

    😕 Bin noch Anfänger! 😃 Mit anderen Worten: HÄ? 😃

    SeppJ schrieb:

    -Bist du sicher, dass 68-72 das machen, was du denkst?

    Was denke ich? Das ich nicht auf diese Variablen von außen aus zugreifen kann, sondern nur in der Klasse und dass diese als Speicher dienen? Ja!

    SeppJ schrieb:

    -Das ganze Verhalten bezüglich underflow und Anzahl und Position der Elemente kommt mir unnötig umständlich vor. Ich bin mir intuitiv recht sicher, dass das wesentlich einfacher ginge.

    Denke ich auch 🤡 , aber ich habe ja auch diesen Stack als Versuchsmodell benutzt.

    SeppJ schrieb:

    -Warum überhaupt den Stack im vorhinein begrenzen? Du benutzt ohnehin dynamische Speicherverwaltung, wieso nicht dynamisch?

    Ich möchte nicht klugscheißen, aber wenn ich jetzt einen heap-Overflow durch eine Schleife verursache, durch z.B. Vergessen eines Zählers (gilt nicht für for), dann würde der Arbeitsspeicher immer voller werden, bis rein theoretisch der Computer abstürzt.
    Oder sehe ich das falsch?

    SeppJ schrieb:

    Dann schalt sie ein!

    Ich weiß nicht, was du für einen Compiler benutzt, doch meiner zeigt keine Fehler oder Warnungen an (diese Funktion ist längst aktiviert) 😕 .

    Bitte nicht den Code für den Destruktor vergessen!

    Danke im Voraus!

    MfG

    Seikuassi

    P.S.: Ich glaube, langsam (oder auch schnell) aber sicher, werde ich hier das schwarze Schaf 😃 .



  • Der Destruktor wird automatisch aufgerufen wenn das Objekt zerstört wird (daher der Name) (bei Objekten auf dem Stack also wenn sie out-of-scope gehen), das ist ja das Tolle dran. Den musst man (normalerweise) nie selbst aufrufen*. Genau so wie der Konstruktor "automatisch" aufgerufen wird, wenn das Objekt erstellt wird.
    RAII: Google
    Heap-Overflow: Wenn das auf einem halbwegs modernen Desktop-OS läuft wird da nicht viel passieren.
    Warnungen: Hast du GCC? Kompilierst du mit -Wall und -pedantic?

    * Tipp: Einfach mal eine Ausgabe rein basteln, dann siehst du wo und wann er aufgerufen wird.



  • Hallo cooky451,

    cooky451 schrieb:

    Der Destruktor wird automatisch aufgerufen wenn das Objekt zerstört wird (daher der Name)

    Ist ja einfacher, als ich gedacht hätte, wobei hätte mir das früher auffallen können 😃 , da in jedem Code von anderen, in dem ein deque, vector o. Ä. aufgerufen wird, kein Member aufruft, der den z. B. Stack zerstört.

    cooky451 schrieb:

    Heap-Overflow: Wenn das auf einem halbwegs modernen Desktop-OS läuft wird da nicht viel passieren.

    char * x;
    INT64 y = 0;
    while ((y < 9223372036854775807)) {
       x [y] = 'a';
       y += 1;
    };
    

    Was wir passieren? Also mein Rechner würde abstürzen (bzw. sich aufhängen). Was macht deiner 😉 ?

    cooky451 schrieb:

    * Tipp: Einfach mal eine Ausgabe rein basteln, dann siehst du wo und wann er aufgerufen wird.

    Gute Idee 👍. Werde ich gleich mal ausprobieren.

    Danke nochmal!

    MfG Seikuassi


  • Mod

    Seikuassi schrieb:

    P.S.: Ich glaube, langsam (oder auch schnell) aber sicher, werde ich hier das schwarze Schaf 😃 .

    Nein, schwarze Schafe sind die, die alles gemacht haben wollen und selber nichts tun. Du bist ziemlich das Gegenteil. Jeder hat mal angefangen und damals kaum was gewusst.

    A propos Nicht-Wissen 😃 :

    Kann mir jemand einen Beispielcode für z.B. den deque oder vector geben?

    Moment! heißt das, du hast das so umständlich gemacht, weil du vector nicht kennst? 😮 Ich dachte bisher, dass du neugierig warst, wie vector wohl intern arbeiten könnte.
    Dann ist es allerhöchste Zeit das zu schnellstens ändern. Die Container der Standardbibliothek sind ungeheuer nützlich und vector ist der nützlichste von allen. Das wird verändern, wie du Programme schreibst!

    Eine Einführung in das Thema ist der Stoff von Lehrbüchern, wenn dir die normalen Codebeispiele in C++-Referenzen nicht ausreichen. Siehe meine Signatur für gute Bücher. Da mindestens "Thinking in C++" gratis erhältlich ist, gibt es keine Ausrede, das nicht zu lesen. Gerade dieses Buch geht an C++ von der komplett anderen Seite ran und fängt mit guten Design an, anstatt mit dem Programmieren an sich. Täte dir vielleicht ganz gut 😉 .

    Andere Stichworte wie "RAII", "Regel der großen 3" und so weiter, die ich genannt habe: Entfallen erst einmal, wenn du STL-Container benutzt, da diese die Techniken bereits benutzen und du somit automatisch von deren "Magie" profitierst.

    Ich möchte nicht klugscheißen, aber wenn ich jetzt einen heap-Overflow durch eine Schleife verursache, durch z.B. Vergessen eines Zählers (gilt nicht für for), dann würde der Arbeitsspeicher immer voller werden, bis rein theoretisch der Computer abstürzt.

    Nein, dein Programm würde abstürzen. Was normales Verhalten bei einem Fehler ist. Ein Betriebssystem bei dem ein fehlerhaftes Programm den ganzen Computer mitreißen kann (zumindest durch diese Art Fehler) musst du heutzutage lange suchen. Dein Programm hat seinen eigenen (virtuellen) Speicher, mit dem kann es machen, was es will.



  • Hallo SeppJ,

    SeppJ schrieb:

    Moment! heißt das, du hast das so umständlich gemacht, weil du vector nicht kennst? 😮

    Keine Sorge, den und deque (und auch andere STL Container) kenne ich und habe auch diese bereits benutzt. Ich wollte einfach nur einen eigenen Stack-Klasse (oder eben Struktur) schreiben, um zu sehen, wie ein Container intern aufgebaut ist (was ist ein Konstruktor, Destruktor usw.).

    Außerdem habe ich gerade in diesem Moment ein C++-Buch neben mir liegen ("Die C++-Programmiersprache" vom Erfinder von C++ 🙂 ).

    SeppJ schrieb:

    Nein, schwarze Schafe sind die, die alles gemacht haben wollen und selber nichts tun. Du bist ziemlich das Gegenteil.

    Oh, jetzt hast du mich an meinen Arbeitsalltag (oder Azubi-Alltag) in meiner Firma erinnert.
    Bei mir entstehen zwar (meistens Flüchtigkeits-) Fehler am Anfang, aber bis jetzt habe ich noch nie Anschiss bekommen, weil ich irgendeine Sache vergessen habe oder die Sicherheit nicht beachtet hätte (würde ich nie tun 😃 🙄 ).
    Ich denke mal, dass bei mir das Problem ist, dass ich meinen Kopf durchsetzen will.

    Beispiel_0: Ihr sagt, ich soll auf irgendetwas achten, ich mach´s aber nicht (jedenfalls nicht vollständig oder nach euren Vorstellungen). Trotzdem kommt nachher die Funktionalität heraus, die ich wollte (eine sehr großmäulige Behauptung 😃 ).
    Beispiel_1: Der Meister sagt zu mir, ich soll auf die Art ein Problem lösen. Ich löse aber das Problem auf meine Art, bei der weder (meistens) Zeit noch Sicherheit vernachlässigt werden. (die Reaktion ist dann meistens wie hier im Forum: "Aber sie sollten doch...","Sie dürfen nicht..","Gut, so geht´s zwar auch, aber..."
    Und mein Meister ist eigentlich zufrieden mit mir 😉 .

    MfG

    Seikuassi


  • Mod

    Seikuassi schrieb:

    char * x;
    INT64 y = 0;
    while ((y < 9223372036854775807)) {
       x [y] = 'a';
       y += 1;
    };
    

    Was wir passieren? Also mein Rechner würde abstürzen (bzw. sich aufhängen). Was macht deiner 😉 ?

    16-Bit DOS im Realmode? Microcontroller ohne OS?

    Jeder "normale" Rechner wird da einfach Segfault melden.



  • Seikuassi schrieb:

    char * x;
    INT64 y = 0;
    while ((y < 9223372036854775807)) {
       x [y] = 'a';
       y += 1;
    };
    

    So ist das mit dynamisch wachsen nicht gemeint.
    Einfach:
    Wenn Stack voll:
    1. neuen Speicher anfordern, Größe = alte_Größe * n (n ist z.B. 2)
    2. alten Speicher in neuen kopieren
    3. alten Speicher freigeben

    Und schon wächst er.



  • Hallo nochmal,

    T0 at (unsigned long long int id,T0 var) {
    	if ((id < 0) || (id >= size)) {
    		error ("at-Funktion außerhalb des Gültigkeitsbereich.",__LINE__,"Stack.h");
    	};
    	if ((init == 0)) {
    		error ("Stack nicht initialisiert",__LINE__,"Stack.h");
    	};
    	if ((id > 0) && (id < size) && (init == 1)) {
    		return mem [id];
    	};
    };
    

    Eines der wohl wichtigsten Container-Funktion, da man jederzeit auf eine Variable auf einer beliebigen Position abfragen, aber auch zuweisen kann.

    Frage: Wie sieht die Funktion (Member) aus, wenn ich einen Wert nicht nur abfragen, sondern auch übergeben will? (wie bei deque, vector etc.)?

    Die Zuweisung soll genau so aussehen, wie bei vector etc.:

    Stack.at (2) = 5; // der Name "Stack" wird später geändert
    

    Danke im Voraus!

    MfG

    Seikuassi



  • Die Geben alle eine Referenz zurück. (T&) Und bitte, schau dir doch erst mal das std::stack Interface an und implementier das. Dann kann man dir auch mal zeigen wie das ordentlich aussieht, bei deinem Kram ist das leider schon konzeptionell zu verkorkst dafür.


  • Mod

    Da meint man, alles gesehen zu haben und dann implementiert jemand einen Stack mit Random access 😃 .@TE: Ich glaube, du solltest nochmal genauer definieren, was dein "Stack" überhaupt können soll.



  • SeppJ schrieb:

    Da meint man, alles gesehen zu haben und dann implementiert jemand einen Stack mit Random access 😃

    Du hast noch nicht in Java programmiert, oder? :p


  • Mod

    Nexus schrieb:

    SeppJ schrieb:

    Da meint man, alles gesehen zu haben und dann implementiert jemand einen Stack mit Random access 😃

    Du hast noch nicht in Java programmiert, oder? :p

    Nicht wirklich viel und das bisschen ist lange her. Mal gucken:

    Java Doku schrieb:

    public class Stack
    extends Vector

    "extends" heißt "ist ein"? Falls ja, dann weint gerade irgendwo ein Professor für abstrakte Datenstrukturen. 😞



  • Hallo SeppJ,

    Da meint man, alles gesehen zu haben und dann implementiert jemand einen Stack mit Random access 😃

    Ich zitiere noch einmal den Code-Kommentar:

    Stack.at (2) = 5; // der Name "Stack" wird später geändert
    

    Ich habe nur Stack als Namen benutzt, da ich vorhin keine Zeit mehr hatte (musste noch schnell wo hin).
    Von mir aus kann man den Namen "Container" oder "Box" verwenden.

    Ich glaube, du solltest nochmal genauer definieren, was dein "Stack" überhaupt können soll.

    Mein "Stack", "Container" oder "Box" soll eigentlich nur 3 Sachen können:

    1.: Konstruieren
    2.: Dekonstruieren
    3.: Random Access-Funktion besitzen (at ())
    4.: (dazu später mehr) statt Zahlen im at (), möchte ich wie bei map Strings als "Adressen" verwenden. Beispiel:

    Statt

    container < int > test (10,0);
    container.at (2) = 50;
    

    möchte ich

    container < int > test {
           "mein_Bezeichner" = 0,
           "mein_Bezeichner0" = 1,
           "mein_Bezeichner1" = 2;
    };
    container.at ("mein_Bezeichner") = 50;
    

    schreiben.

    Mehr Funktionen können theoretisch nicht schaden (push_back etc.).
    Jetzt denkt wahrscheinlich jeder: Warum nimmt der nicht einfach einen deque oder vector. Da hat er doch alles. 😕

    Die Antwort ist ganz einfach: Ich möchte einfach nur lernen, wie eben die STL Container grob funktionieren. 😃

    Also wie muss jetzt die at ()-Funktion aussehen, damit ich sowohl Werte zurückbekomme, als auch definieren kann?

    Danke im Voraus!

    MfG

    Seikuassi



  • Ups, kleiner Fehler!

    Statt

    container < int > test (10,0);
    test.at (2) = 50;
    

    möchte ich

    container < int > test {
           "mein_Bezeichner" = 0,
           "mein_Bezeichner0" = 1,
           "mein_Bezeichner1" = 2;
    };
    test.at ("mein_Bezeichner") = 50;
    

    schreiben.

    Wie schon gesagt: Dazu kommen wir später. Erstmal ist die at-Funktion wichtig.



  • @Seikuassi

    Du bist heslig! Du Smog.


  • Mod

    Seikuassi schrieb:

    Also wie muss jetzt die at ()-Funktion aussehen, damit ich sowohl Werte zurückbekomme, als auch definieren kann?

    cooky451 schrieb:

    Die Geben alle eine Referenz zurück. (T&) Und bitte, schau dir doch erst mal das std::stack Interface an und implementier das. Dann kann man dir auch mal zeigen wie das ordentlich aussieht, bei deinem Kram ist das leider schon konzeptionell zu verkorkst dafür.

    Am besten in einer const und einer nicht-const Version.


Anmelden zum Antworten