Arbeit mit Datenstrukturen - ZAHLENOBJEKT



  • Null = (TZahl*)malloc(sizeof(TZahl)); 
      Null->vkStellen = 0; 
      Null->nkStellen = 0; 
      // Vorkommastelle mit 0 initialisieren 
      Null->vkStelle = (TZiffer*)malloc(sizeof(TZiffer)); 
      Null->vkStelle->ziffer = 0; 
      Null->vkStelle->links = 0; 
      // Nachkommastelle erzeugen 
      Null->vkStelle->rechts = (TZiffer*)malloc(sizeof(TZiffer)); 
      Null->vkStelle->rechts->links = Null->vkStelle; 
      Null->vkStelle->rechts->rechts = 0; 
      Null->vkStelle->rechts->ziffer = 0;
    

    Wenn du weißt, was hier passiert und eine Vorstellung hast, weißt du, wie du die ersten 4 Funktionen schaffst. Die anderen sind ein bisschen anders, aber auch so ähnlich.
    Ein sehr gutes onlinetutorial über die Zeiger und der dynamischen Speicherverwaltung und dynamischen Datenstrutktoren gibt es hier::

    http://www.hs-augsburg.de/~sandman/c_von_a_bis_z/c_014_000.htm#Xxx999328

    Links ist das Inhaltsverzeichniss. Wenn du das hier durchgehst(ich selber brauchte nur 60-90 minuten), brauchst du für die ersten 4 Funktionen maximal(!) nur 1h. 😉

    MFG Majin_Clodan



  • Erstmal vorne weg: Bei dem Code, den Du als 'Vorgaben' gepostet hast, handelt es sich fast ausschließlich um C (ausgenommen der Deklaration der structs). Und selbst wenn Du soviel lernst, dass Du diese Aufgabe zu 100% und ganz zügig lösen kannst, so hast Du anschließend von C++ praktisch keine Ahnung.
    Die Aufgabestellung lehnt sich auch in keiner Weise an Vorgehensweisen an, die in C++ üblich sind (z.B. Einsatz von Klassen/Strukturen und Container-Schnittstellen).

    Zur Erklärung:
    - TZahl* erzeugeNull()
    erzeugt ein Objekt von TZahl und initialisiert es. Ebenso wird jeweils ein Objekt von TZiffer für je eine Vor- und ein Nachkommastelle erzeugt und initialisiert. Die Anzahl wird jeweils mit 0 initialisiert, obwohl jeweils eine Stelle existiert (?) Der Member aktpos ist vergessen worden.

    - TZahl* read(char* text)
    es wird ein TZahl erzeugt (s.o.) dann wird solange zifferVorKomma mit dem Wert der Ziffer auf dem erzeugten TZahl-Objekt aufgerufen, bis ein Dezimal-Trenner erscheint oder der String zu Ende ist. Ist der Test zu Ende wird die Funktion verlassen. Falls nicht 'spult' er den Zeiger 'text' auf den Text bis zum Textende und ruft dann von hinten für jede Ziffer ZifferNachKomma auf. (?)

    - void druckeZahl(TZahl* zahl)
    besorgt sich mit posLinks einen Zeiger auf das TZiffer-Objekt. In der Schleife wird solange der Wert der Ziffer ausgeben und der Zeiger mit nachRechts weiter geschaltet bis dieser zu 0 wird. Wenn derweil irgendwann die Funktion ersteNKStelle true liefert, so wird noch ein '.' eingefügt.



  • Danke zunächst für eure Antworten!

    @Majin_Clodan
    Das Tutorial ist wirklich ganz gut. Ich hab es nun durchgearbeitet. Ich habe mit der Funktion posLinks begonnen. Der Zugriff auf die Elemente ist mir nun klar. Nur wie kann ich jetzt den Zeiger auf die "linkeste" Ziffer setzen??

    Mein bisheriger Ansatz dafür:

    TZiffer* posLinks(TZahl* zahl)
    {
    zahl = (TZahl*)malloc(sizeof(TZahl)); // Speicherreservierung
    zahl->aktPos->links;                  // Hier versuch ich den Zeiger zu setzen
    }
    

    Was mach ich falsch?



  • BartMan schrieb:

    zahl->aktPos->links;                  // Hier versuch ich den Zeiger zu setzen
    

    Was mach ich falsch?

    In dieser Zeile machst du gar nichts. Da könntest du genausogut "3;" in die Zeile schreiben. Da steht nur ein Wert, der keiner Variable zugewiesen wird. Der Compiler wird eine solche Zeile wegkürzen, da ohne Funktion. Tipp: "=" ist der Zuweisungsoperator! 😉



  • BartMan, hast du Werners Beitrag auch gelesen? Das ist kein bisschen C++, was du da machst.

    Wieso verwendest du nicht new und delete ? Oder Klassen? Oder C++-Cast-Operatoren wie static_cast ? Oder gleich Container?



  • TZiffer* posLinks(TZahl* zahl) 
    { 
    zahl = (TZahl*)malloc(sizeof(TZahl)); // Speicherreservierung 
    zahl->aktPos->links;                  // Hier versuch ich den Zeiger zu setzen 
    }
    

    Ja, wie gesagt, wo zeigst du den Zeiger hin?
    Ich gebe dir mal was vor und du machst dann einfach weiter:

    TZiffer* neuZiffer = (TZiffer*)malloc(sizeof(TZiffer)); /**Neuer Objektblock**/
    
        neuZiffer->links = zahl->vkStelle;   /**Zeigung Links vom neuen Objektblock ausgehend**/
    
        neuZiffer->rechts = zahl->vkStelle->rechts; /**Zeigung rechts vom neuen Objektblock ausgehend**/
    
        neuZiffer->ziffer = ziffer; /**Wert im Block, der erstellt wurde**/
    

    Versuche zu verstehen, was hier passiert und wieso es so ist. Damit die Funktion nun vollständig wird, fehlen nur noch 2 Zeilen. 😉 Aber wenn du das oben verstehst, wirst du das auch hinbekommen. 🙂

    MFG Majin_Clodan

    PS: Ich habe es in C programmiert. Kannst auch in C++ machen, aber damit hab ich bisher zu wenig gearbeitet.



  • #include <stdio.h>
    #include <stdlib.h>
    
    struct TZiffer {
      char ziffer;
      TZiffer* links;
      TZiffer* rechts;
    };
    
    struct TZahl {
      TZiffer* vkStelle; // Vorkommastelle
      int nkStellen;     // Nachkommastellen
      int vkStellen;     // Vorkommastellen
      TZiffer* aktPos;   // aktuelle Position in der liste
    };
    
    // Erzeugt den Dezimalbrucch 0.0
    TZahl* erzeugeNull()
    {
      TZahl* Null;
    
      Null = (TZahl*)malloc(sizeof(TZahl));
      Null->vkStellen = 0;
      Null->nkStellen = 0;
      // Vorkommastelle mit 0 initialisieren
      Null->vkStelle = (TZiffer*)malloc(sizeof(TZiffer));
      Null->vkStelle->ziffer = 0;
      Null->vkStelle->links = 0;
      // Nachkommastelle erzeugen
      Null->vkStelle->rechts = (TZiffer*)malloc(sizeof(TZiffer));
      Null->vkStelle->rechts->links = Null->vkStelle;
      Null->vkStelle->rechts->rechts = 0;
      Null->vkStelle->rechts->ziffer = 0;
    
      return Null;
    }
    
    // Gibt 1 zurueck, falls aktPos auf die erste Nachkommastelle zeigt
    int ersteNKStelle(TZahl* zahl)
    {
      return zahl->aktPos == zahl->vkStelle->rechts;
    }
    
    void zifferVorKomma(char ziffer, TZahl* zahl)
    {
      int x = ziffer - '0';
      TZahl* AddL;
    
      AddL = (TZahl*)malloc(sizeof(TZahl));
      ++(AddL->vkStelle);
      AddL->vkStellen = (zahl->vkStellen) +1;
      AddL->nkStellen = (zahl->nkStellen);
      zahl->aktPos = AddL->aktPos;
      // Vorkommastelle mit ziffer initialisieren
      AddL->vkStelle = (TZiffer*)malloc(sizeof(TZiffer));
      AddL->vkStelle->links = zahl->vkStelle;
      AddL->vkStelle->ziffer = x;
      AddL->vkStelle->rechts = 0;
    }
    
    void zifferNachKomma(char ziffer, TZahl* zahl)
    {
      int y = ziffer - '0';
      TZahl* AddR;
    
      AddR = (TZahl*)malloc(sizeof(TZahl));
      AddR->vkStellen = (zahl->vkStellen);
      AddR->nkStellen = (zahl->nkStellen) +1;
      ++(AddR->aktPos);
      AddR->vkStelle = zahl->vkStelle;
      // Nachkommastelle mit ziffer initialisieren
      AddR->vkStelle->rechts = (TZiffer*)malloc(sizeof(TZiffer));
      AddR->vkStelle->rechts->links = zahl->vkStelle->links;
      AddR->vkStelle->rechts->rechts = zahl->vkStelle;
    //  for (AddR->nkStellen !=0) {
    
     // }
    
      AddR->vkStelle->rechts->ziffer = y;
    
    }
    
    TZiffer* posLinks(TZahl* zahl)
    {
      while (zahl->aktPos->links)
          --(zahl->aktPos);
      return  zahl->aktPos;
    }
    
    TZiffer* posRechts(TZahl* zahl)
    {
       while (zahl->aktPos->rechts)
          ++(zahl->aktPos);   
       return  zahl->aktPos;
    }
    
    TZiffer* nachRechts(TZahl* zahl)
    {
             if (zahl->aktPos->rechts) ++(zahl->aktPos);
             else return 0;
    }
    
    TZiffer* nachLinks(TZahl* zahl)
    {
             if (zahl->aktPos->links) --(zahl->aktPos);
             else return 0;
    }
    
    void normalisiere(TZahl* zahl)
    {
          if (zahl->aktPos->links) (zahl->aktPos)--;
          //for (1) {
              //free (AddL->aktPos);
              //if ((zahl->aktPos->links->ziffer) != '0') break;
          //}
    }
    
    // Zum Erzeugen eines Zahlenobjekts aus einem Text
    // Zum Beispiel: text = "12.500600"
    TZahl* read(char* text)
    {
      TZahl* zahl;
    
      zahl = erzeugeNull();
    
      while(*text != '.' && *text) {
        zifferVorKomma(*text - '0',zahl);
        text++;
      }
    
      if(!*text)
        return zahl;
    
      while(*text)
        text++;
    
      text--;
      while(*text != '.') {
        zifferNachKomma(*text - '0',zahl);
        text--;
    
      }
      return zahl;
    
    }
    
    // Zum Ausgeben der Ziffern eines Zahlenobjekts einschliesslich
    // des Kommas
    void druckeZahl(TZahl* zahl)
    {
      TZiffer* ziffer;
    
      ziffer = posLinks(zahl);
    
      do {
        if(ersteNKStelle(zahl))
          printf(".");
        printf("%c",ziffer->ziffer + '0');
        ziffer = nachRechts(zahl);
      }while(ziffer);
    }
    
    int main(int argc, char* argv[])
    {
      char eingabe[1024];
      TZahl* zahl;
    
      // Zum Testen der Implementierung. Die eingegebene Zahl darf nur aus
      // Ziffern und einem Punkt bestehen und sollte einen Vor- und Nachkommaanteil 
      // haben, die durch einen Punkt getrennt sind. Zum Beispiel: 47.11
      gets(eingabe);
      zahl = read(eingabe);
      druckeZahl(zahl);
      printf("\n");
      normalisiere(zahl);
      druckeZahl(zahl);
      printf("\n");
    }
    

    😞 Hilfe XD



  • kibi_kibi schrieb:

    😞 Hilfe XD

    wobei? konkreten Fragen wären hilfreich.

    Der Code enthält einige schwere Fehler. Z.B.:

    kibi_kibi schrieb:

    TZiffer* posLinks(TZahl* zahl)
    {
      while (zahl->aktPos->links)
          --(zahl->aktPos);
      return  zahl->aktPos;
    }
    

    wenn Du in einer Liste einen Pointer 'weiter schaltest' so wird dieser nicht in- oder dekrementiert, sondern auf den Pointer gesetzt, den das Listen-Element vorgibt. Z.B.

    zahl->aktPos = zahl->aktPos->rechts; // aktpos auf das rechts stehende Element weiterschalten
    

    und die Rückgabe der ganz links - also am Anfang - stehenden Ziffer sollte mit

    // Setzt den Zeiger aktPos im Zahlenobjekt zahl auf die am weitesten links stehende Ziffer in diesem Zahlenobjekt und gibt einen Verweis auf diese Ziffer zurück
    TZiffer* posLinks( TZahl* zahl )
    {
        return zahl->aktPos = zahl->vkStelle;
    }
    

    .. zu erledigen sein.

    Gruß
    Werner

    PS.: bist Du BartMan?



  • Werner Salomon schrieb:

    PS.: bist Du BartMan?

    hehe nein ;P aber wir haben das gleiche Aufgabe.
    Danke für Hilfe.
    Meine konkrete Frage: was ist hier falsch?.... in Fuktionen ersteNKStelle(TZahl* zahl), zifferVorKomma(char ziffer, TZahl* zahl), zifferNachKomma(char ziffer, TZahl* zahl), nachRechts(TZahl* zahl), nachLinks(TZahl* zahl), normalisiere(TZahl* zahl).

    Ich habe versucht das zu schreiben, aber bis jetzt weiß ich nicht, ob ich das richtig mache oder nicht XD.



  • kibi_kibi schrieb:

    Meine konkrete Frage: was ist hier falsch?.... in Fuktionen ersteNKStelle(TZahl* zahl), zifferVorKomma(char ziffer, TZahl* zahl), zifferNachKomma(char ziffer, TZahl* zahl), nachRechts(TZahl* zahl), nachLinks(TZahl* zahl), normalisiere(TZahl* zahl).

    Ich habe versucht das zu schreiben, aber bis jetzt weiß ich nicht, ob ich das richtig mache oder nicht XD.

    - ersteNKStelle (s.o.)
    - zifferVorKomma "Fügt in das übergebenen Zahlenobjekt zahl die übergebene Ziffer ziffer unmittelbar vor dem Komma ein. Alle anderen Ziffern vor dem Komma verschieben sich in dem Zahlenobjekt um eine Position nach links. Der Nachkommaanteil bleibt unverändert. ziffer speichert wieder den Wert der Ziffer und nicht das Zeichen. "

    void zifferVorKomma(char ziffer, TZahl* zahl)
    {
      int x = ziffer - '0';
      // AddL = (TZahl*)malloc(sizeof(TZahl)); // Du must eine neue TZiffer erzeugen, eine TZahl hast Du ja schon
      // ++(AddL->vkStelle);  // <- das geht überhaupt nicht (s. mein Kommentar im letzen Posting)
    
      // -- Ziffer erzeugen und alle Member belegen
      TZiffer* AddL = new TZiffer;
      AddL->ziffer = x;
      TZiffer* letzteVkStelle = zahl->vkStelle;
      for( int vk = zahl->vkStellen; vk > 0; --vk, letzteVkStelle = letzteVkStelle->rechts )
          ; // letzteVKstelle über alle VK-Stellen laufen lassen, bis zur letzten; Achtung: führende 0 beachten!
      AddL->links = letzteVkStelle;
      AddL->rechts = letzteVkStelle->rechts;
    
      // -- dann die Zeiger zwischen 'letzteVKstelle' und seinem rechten Nachfolger umhängen
      letzteVkStelle->rechts->links = AddL;
      letzteVkStelle->rechts = AddL;
    
      // -- und mitzählen nicht vergessen
      ++(zahl->vkStellen);
    }
    

    (ohne Gewähr)

    BTW: was bedeute der Member TZahl::vkStellen genau?

    - zifferNachKomma
    so ähnlich sie zifferVorKomma, versuch's mal selber.

    - nachRechts, nachLinks
    siehe mein letztes Posting

    - normalisiere(TZahl* zahl)
    "Implementieren Sie eine Funktion void normalisiere(TZahl* zahl), die führende und abschließende Nullen – bis auf jeweils eine – in einem Zahlenobjekt streicht oder ergänzt, falls keine führende/abschließende 0 vorhanden ist. "

    void normalisiere( TZahl* zahl )
    {
        for( TZiffer* digit = zahl->vkStelle; zahl->vkStellen > 0 && digit->rechts->ziffer == 0; --(zahl->vkStellen) )
        {
            // rechts von der ersten 0 steht auch eine 0 also erstere entfernen
            digit->rechts->links = 0; // rechte 0 wird zur ersten 0
            zahl->vkStelle = digit->rechts;
            free( digit );
            digit = zahl->vkStelle;
        }
        TZiffer* digit = zahl->vkStelle;
        for( ; digit->rechts != 0; digit = digit->rechts )
            ; // letze Nachkommastelle suchen
        for( ; zahl->nkStellen > 0 && digit->links->ziffer == 0; --(zahl->nkStellen) )
        {
            // links von der letzen 0 steht auch eine 0 also die letzte entfernen
            digit->links->rechts = 0; // linke 0 wird zur letzen 0
            TZiffer* tmp = digit;
            digit = digit->links;
            free( tmp );
        }
    }
    

    (auch nicht getestet)
    Gruß
    Werner


Anmelden zum Antworten