String einlesen und als nicht durch 0 terminiertes char-Array speichern, genaueres Inside...



  • Man merkt, dass du von Java kommst 😉
    Das mit dem sizeof(array) funktioniert so (in diesem Fall) in C++ leider nicht...
    Um die Länge eines nullterminierten char-arrays festzustellen, bleibt dir wohl nichts anderes übrig, als die strlen()-Funktion aus der C-Standardbibliothek zu verwenden, oder das Verhalten dieser nachzubauen:

    void MyString::ComputeLength(const char* array)
    {
       mSize = strlen(array) - 1;
    
       //bzw.
    
       while(*array != '\0')
       {
         mSize++;
         array++;
       }
    }
    

    In beiden Fällen hab ich jetzt angenommen, dass du das Nullbyte nicht mitkopieren möchtest.

    Jetzt kannst du dynamisch Speicher anfordern, um das Array aufzunehmen:

    void MyString::AllocateSpace()
    {
        mInnerArray = new char[mSize]; //mInnerArray ist vom Typ char*
    
    }
    

    Und weil C++ nicht Java ist, musst du den Speicher beim Zerstören des Objektes auch wieder freigeben 😉

    MyString::~MyString()
    {
       ...
       delete[] mInnerArray
    
    }
    

    Grüße

    Martin



  • LogicCube schrieb:

    ...
    Hat vielleicht jemand einen "Tip" wie man das Array für die eingelesene Zeichenkette dynamisch erstellen kann so dass direkt passend viele Stellen erzeugt werden? ...

    Japp - ist nicht schwer:

    class TestObjekt {
       public:
            TestObjekt(char const x[]);
            ~TestObjekt();
            void setWert(char const y[]);
            char* getWert();
       private:
           char *x_hide;
           size_t len;
    };
    TestObjekt::TestObjekt(char const x[]) 
       : x_hide(new char[strlen(x)), len(strlen(x)) {
       strcpy(x_hide,x);
    }
    
    TestObjekt::~TestObjekt(){ 
       delete[] x_hide; // hier solltest Du löschen, weil obe dynamisch alloziert
    }
    // ...
    

    ACHTUNG: Dieser Code ist hochgradig unsicher !!!
    Folgendes z.B. führt zu undefiniertem Verhalten:

    int main() {
       TestObjekt a("Simon2");
       a.setWert("mehr Bytes"); // -> BOOM
       delete[] a.getWert(); // -> Hoppla ! Objekt hat keinen Speicher mehr und weiß es nichtmal
    ...
    

    Hilfen könnten sein:
    - setWert() kopiert höchsten len viele Zeichen oder
    - setWert() alloziert neuen Speicher (und kopiert und delete[]-t den alten)
    - getWert() liefert einen char const* zurück

    Prinzipiell sind char*-Geschichten immer unsicher ... es garantiert Dir z.B. niemand, dass der Initialwert überhaupt auf einen 0-terminierten String verweist.
    Schön versteckt:

    char c[6] = "Simon2"; // 0-Byte passt nicht mehr rein
       TestObjekt a(c); // Hoppla ....
    

    EDIT:

    JimmydaMage schrieb:

    ...

    mSize = strlen(array) - 1;
    

    ...

    stimmt nicht . strlen() zählt NICHT das 0-Byte mit.

    Gruß,

    Simon2.



  • Hey,

    das hört sich vom Ansatz her schonmal sehr gut an! Auch das mit dem nicht mitkopieren der terminierungs-0 am ende; jetzt könnte man ja im prinzip zunächst ausgeben wie lang die zeichenkette MIT 0 ist, und dann wenn man die zeichenkette zwischenspeichert in der klasse String (selbstgebaut), nochmal eine ausgabe (die ja dann logischerweise genau um 1 kleiner wäre) machen um zu zeigen das man wirklich die erminierungs-0 wegläßt!

    ich muss jetzt zur arbeit (ja studenten brauchen auch geld 🙂 ) und werde mich dann sobald ich abends nach hause komme dran machen das ganze zu implementieren; es ist zwar noch ein weiter weg, aber dafür bin ich nachher umso besser gelaunt wenn es dann tatsächlich kompett funktionieren sollte 🙂

    Vielen Dank schonmal, bis heute abend!



  • [quote="Simon2"]

    LogicCube schrieb:

    ...

    class TestObjekt {
       public:
            TestObjekt(char const x[]);
            ~TestObjekt();
            void setWert(char const y[]);
            char* getWert();
       private:
           char *x_hide;
           size_t len;
    };
    TestObjekt::TestObjekt(char const x[]) 
       : x_hide(new char[strlen(x)), len(strlen(x)) {
       strcpy(x_hide,x);
    }
                     
    TestObjekt::~TestObjekt(){ 
       delete[] x_hide; // hier solltest Du löschen, weil obe dynamisch alloziert
    }
    // ...
    

    .

    Zum kopieren kannst du nicht strcpy() verwenden da dass ja das \0 mitkopiert und dafür hast du keinen Platz reserviert BOOOOM 😃 . Ich würde hier memcpy() verwenden

    TestObjekt::TestObjekt(char const x[]) 
       : x_hide(new char[strlen(x)), len(strlen(x)) {
       memcpy(x_hide,x,len);
    }
    


  • Tanren schrieb:

    ...
    Zum kopieren kannst du nicht strcpy() verwenden da dass ja das \0 mitkopiert ...

    Ist auffallend korrekt ! 👍
    Habe mir das gar nicht weiter angesehen, sondern einfach stehengelassen ... macht's aber nicht richtiger.

    Merke: Diesen ganzen Kram spart man sich mit std::string. 😃

    Gruß,

    Simon2.



  • Oder einfach std::strncpy nutzen und länge ohne Nullterminiernugszeichen angeben ... http://www.cppreference.com/stdstring/strncpy.html ... steht nur das das Nullterminiert wird, wenn es kürzer ist 😛



  • (D)Evil schrieb:

    Oder einfach std::strncpy nutzen und länge ohne Nullterminiernugszeichen angeben ... http://www.cppreference.com/stdstring/strncpy.html ... steht nur das das Nullterminiert wird, wenn es kürzer ist 😛

    Für sowas ist aber eher memcpy gedacht...



  • Jo, memcpy oder einfach for() 🙂

    z.T. Null-Terminator nicht mitkopieren: wie soll man dann den Getter Implementieren der ja "ein nullterminiertes Array" (ich schätze gemeint ist ein Zeiger) zurückgeben soll...?

    BTW @LogicCube: um Misverständnisse zu vermeiden gewöhne dir an sowas:

    void MyFunction(char const a[])
    {
    // ...
    }
    

    lieber so zu schreiben:

    void MyFunction(char const* a)
    {
    // ...
    }
    

    Gerade ein Anfänger könnte sonst meinen dass er ein Array übergeben bekommt (was nicht der Fall ist, das Array wird nicht kopiert), oder dass er mit sizeof(a) die Länge des Arrays bestimmen könnte (was auch nicht der Fall ist).
    Übergeben wird einfach ein Zeiger auf die Originaldaten, so wie es in der 2. Version klar ersichtlich ist.
    (BTW: beide Schreibweisen sind 100% austauschbar und gleichbedeutend)

    Ich persönlich verwende die "char const[]" Syntax für Parameter *nie*, ist mir viel zu verwirrend 🙂
    (Ich schreibe z.B. auch nicht "char* argv[]" sonder "char** argv".)



  • Hab die selbe Aufgabe, wahrscheinlich soga den selben Prof. 😉

    Komm allerdings eher aus der Java und C# Richtung deswegen komm ich voll nich kla mit dem : und :: hab noch nit all zu veil C++ gemacht deswegen brauhc ich da ein kleinen schupser in die Rictige richtung.

    btw: wenn ich memcpy mache dann nimmt der bei mir irgendein komishcen speicherraum, ich guck nochmal rüber aber irgendwie kopiert der den falschen inhalt welcher auch komischer weise noch viel größer ist.



  • real12334 schrieb:

    ...brauhc ich da ein kleinen schupser in die Rictige richtung. ...

    *schubs*



  • Habs, is zwar irgendwie was simple aber es klappt ... atte zuerst voll probleme, weil ich bei der rückgabe des \0 nich wieder angefügt habe ^^ 🙂

    #include <iostream>
    #include <math.h>
    using std::cout;  
    using std::cin;
    using std::string;
    
    using namespace std;
    
    class String
    {
    		char* internarray;
    		int laenge;
          public:
    		  String(char* param)
    		  {
    			laenge = strlen(param);	
    			internarray = (char*) malloc(laenge*sizeof(char));
    			make_string(param);
    			//memcpy(internarray,param,laenge);
    
    		  }
    		  void make_string(char* str)
    		  {
    			  int i = 0;
    			  while(str[i]!='\0')
    			  {
    				internarray[i] = str[i];
    				i++;
    			  }
    		  }
    
    		  ~String()
    		  {
    			delete[] internarray;
    		  }
    
    		  char* get_string()
    		  {
    			internarray[laenge]='\0';
    			return(internarray);
    		  }
    
    };
    
    int main() {
       String* str;
       char eingabe[30];
       cin >> eingabe;
       str = new String(eingabe);  
       cout << "Der wahr string kommt jetzt ! : " << (char*)str->get_string();
       cin >> eingabe;
    
       return 0;
    }
    


  • Ne, nicht ganz.. In get_string hängst du eine '\0' an, wofür eigentlich kein Speicher mehr da ist. Also entweder ein char mehr allokieren oder bei get_string nochmal Speicher holen, den Inhalt deines Strings reinkopieren und da dann die 0 anhängen.



  • @real1231231:
    probier mal das aus, das sollte auch gehen (geht aber mit deiner klasse nicht):

    void x()
    {
        String a("test234");
        String b(a);
    } // <- BOOM
    
    void y()
    {
        String a("test234");
        String b;
        b = a;
    } // <- BOOM
    


  • hab hier :

    String(char* param)
              {
                laenge = strlen(param);   
                internarray = (char*) malloc(laenge*sizeof(char));
                make_string(param);
    

    ja extra kein -1 an der länge gemacht damit noch für 1 zeichen mehr platz ist, so ist glaub ich genug speicher vorhanden



  • @hustbaer
    der copy konstruktor soll in aufgabenteil c) ja auch von uns neu geschrieben bzw. überladen werden, noch kein peil wie aber ich setz mich mal dran, bisher funktioniert alles, den + operator hab ich auhc schon überladen funktioniert auch alles, nur hab ich noch en problem mim dynamischen CHar array erstellen.



  • real12313 schrieb:

    hab hier :

    String(char* param)
              {
                laenge = strlen(param);   
                internarray = (char*) malloc(laenge*sizeof(char));
                make_string(param);
    

    ja extra kein -1 an der länge gemacht damit noch für 1 zeichen mehr platz ist, so ist glaub ich genug speicher vorhanden

    Ne, strlen zählt nur die Zeichen bis zum '\0', also müsstest du für die korrekte Länge noch eins draufaddieren 👍



  • @real1231231: Ok. Ich weiss nicht wie euer Lehrer/Vortragender/... das haben möchte, aber normalerweise sollte man den copy-ctor und den assignment operator "private" machen wenn die automatisch vom Compiler erstellten Versionen "nicht passen" und man selbst keine geeignete Definition schreiben kann oder will. Damit sie eben nicht aufgerufen werden können - so können keine unbeabsichtigten Fehler passieren.

    Also entweder eine funktionierende Definition selbst schreiben, oder private machen.

    Die Definition von so einem privaten copy-ctor/assignment operator lässt man üblicherweise auch weg, da sie ja sowieso nie aufgerufen werden können. Steht doch irgendwo ein Aufruf (z.B. IN der Klasse selbst wäre das ja möglich) bekommt man so einfach einen Linker-Error, was auch gut ist.

    Sieht ca. so aus:

    class String
    {
    public:
        // ...
    
    private:
        String(String const&); // not copyable
        String& operator =(String const&); // not assignable
    
        // ...
    };
    


  • Hey,

    so ich hatte jetzt 2 Wochen kein Internet und konnte daher erst jetzt die Diskussion und die Aufgabe weiter verfolgen. Ich habe mir jetzt den Quelltext von real mal angeguckt, und die Ausgaben bzw. das programm verfolgt, aber es ist nicht ganz ersichtlich bei den Ausgaben auf dem Bildschirm was genau passiert.

    Daher wäre es schön wenn er mal seinen Quelltext posten würde, damit man das Ganze mal vergleichen kann.



  • ich persönlich mag die sehr genaue formulierung der aufgabe weil sie keinen zweifel lässt was gewünscht wird abgesehen davon, dass man die formulierung erstmal verstehen muss 🤡

    wenn ich das richtig verstanden habe soll nur die interne darstellung nicht zero-terminiert sein, das per get zurückgegeben char-array schon. warum nicht ein temporales char-array erzeugen daß eine copy vom internen char-array ist und zur sicherheit an letzter stelle die Null schreiben (ist sie nicht per default da?).

    Rückgabe soll ein Array sein ... geht denn das?
    Ich habs selber noch nit hingekriegt ausser Zeiger auf Arrays.
    Wenn es geht, dann rate ich dir dringend es nicht anders zu machen!!!
    Alles andere wären Minus-Punkte!!!
    Er würde es nicht so messerscharf formulieren, wenn er es nicht auch genau so meinen würde!!!
    (daher denke ich es geht!)


Anmelden zum Antworten