Wie einen char* richtig returnen?



  • Hi folks!
    Sry, wenn die frage zu primitiv erscheint, ich habe hier jetzt jedoch folgendes problem:

    Ich habe eine klasse, diese klasse hat eine methode, die bestimmte werte der member-variablen in ein string schreiben und zurückgeben soll, etwa so:

    class myClass{
    public int a,b,c;
      //... lauter tolle und nützliche sachn
      //die funktion [funktioniert nicht richtig]
      char* toString(){
        char szOutput[64];
        sprintf(szOutput, "Values: a= %i b= %i c= %i", a,b,c);
        return szOutput;
      }
    };
    

    selbstverständlich kann diese funktion nicht richtig funktionieren, weil szOutput als lokale variable auf dem stack erzeugt wird, und gleich nach dem verlassen der funktion wieder mit anderem schrott vollgeschrieben wird. Das meldet der compiler auch als warnung ("zeiger auf eine lokale variable wird zurückgegeben")
    Doch wie soll ich das jetzt machen, sodass ich der funktion keinen fertigen buffer übergeben muss (will die länge des buffers nicht nochmal ausserhalb der funktion angeben, für den fall, dass sich was ändert) [So solls nicht aussehen:]

    void toString(char* szOutput){
      sprintf(szOutput, "Values: a= %i b= %i c= %i", a,b,c);
    }
    
    //und dann irgendwo verwenden:
    char buffer[64]; //oder doch 128... oder 256... oder was?
    myObject.toString(buffer);
    cout<<buffer<<endl;
    

    Und das char array irgendwo auf den heap zu schmeissen will ich auch nicht: es wird zwar nicht gleich wieder mit schrott überschrieben, doch dann weis ich nicht, wie ich den array wieder freigeben soll...

    Könnte mir jemand irgendeinen guten ratschlag geben, wie dieses problem am besten zu lösen ist? Dank im voraus.

    greetz...



  • Entweder der C++-Weg:

    string toString() {
        ostringstream ss;
        ss << "Values: a=" << a << ...;
        return ss.str();
    }
    

    Oder mit einem statischen Puffer (ist nicht Threadsafe, zweiter Aufruf zerstört Ergebnis vom ersten Aufruf):

    char const* toString() {
        static char buffer[64];
        snprintf(buffer, sizeof(buffer), ...);
        return buffer;
    }
    

    Oder genau so wie es nicht aussehen soll 🤡 :

    int toString(char* buffer, size_t length) {
       return snprintf(buffer, length, ...);
    }
    


  • Ist ja lustig. Innerhalb von 20 Minuten 2 Fragen zu den selben Problem 🤡



  • Okay, danke schonmal!
    Was die erste lösung angeht: dat versteh ich nich, diese ganzen strings sind irgendwelche schräge dinger, wo man unendlich lang was reinschreiben kann, die dann aber trotzdem ordnungsgemäß von dem stack entfernt werden. Voll praktisch, voll toll, aber ich versteh noch nich, wie die funktionieren, deswegen lass ichs zunächstma (vllt werde ich irgendwann schlauer^^) :p

    Was die zweite lösung angeht: für ein singleton wär's ganz okay, aber in meinem fall unbrauchbar.

    Also gut, überredet, ich nehm die dritte (so wie ichs nicht haben wollte 😃 )
    Wenn ich zusätzlich ne buffer-länge übergebe, kann die funktion zumindest nicht quer durch den stack irgendwelche romane schreiben, damit kann ich schon ganz gut leben 👍

    Danke sehr 🙂



  • @ tntnet... sry, hab deinen beitrag übersehn... was war die zweite frage? moment muss ich ma gugn 😃



  • Hallo Andrey,

    falls du das Problem mit eigenen Objekten hast, gib nen std::auto_ptr mit
    einem auf dem Stack erzeugten Objekt zurück, dann wird das Objekt beim verlassen
    des Scopes automatisch gelöscht...

    Etwa so:
    std::auto_ptr<Foo> getObject() {
    return std::auto_ptr<Foo>(new Foo());
    }

    {
    std::auto_ptr<Foo> obj = bar->getObject();
    } // Hier bei der schliessenden Klammer wird das Obj zerstört (Scope)



  • Hm... Da muss ich erstma überlegen, wie ich das in meinem fall anwenden soll, aber trotzdem danke, werde mal in der dokumentation n bisschen blättern 🙂



  • Nur dass Du Dich nicht zu Tode blätterst: Ich kann schonmal vorwegnehmen, dass auto_ptr nicht für Arrays (und damit auch nicht für C-Strings) geeignet ist.



  • 😃 👍



  • eine andere relativ gute möglichkeit, wie ich finde ist noch diese:

    char* ToString(int nummer, char* buffer)
    {
      sprintf(buffer, "%d", nummer);
      return buffer;
    }
    

    So musst du zwar trotzdem einen Buffer erstellen, allerdings kannst du die Funktion trotzdem direkt in Ausdrücken verwenden. Programmiert sich um einiges leichter.



  • jojo, hab ich natürlich gemacht, erspart echt ne menge schreibarbeit 👍



  • LordJaxom schrieb:

    Entweder der C++-Weg:

    string toString() {
        ostringstream ss;
        ss << "Values: a=" << a << ...;
        return ss.str();
    }
    

    da wird aber der ganze string kopiert



  • Jaja, und allokiert, und....

    Bleibt aber trotzdem der sauberste C++-Weg 😉
    Vom nötigen Aufwand her ähnlich und trotzdem sicher wäre wohl in diesem Fall nur ein boost::shared_array oder ein noch zu entwerfender auto_array, was wohl (gerade für den OP) etwas Overkill wäre.



  • Habe jetzt eine recht komfortable und für mich durchaus akzeptable(was die fehleranfälligkeit angeht) lösung gefunden. Die lautet: singelton klasse, die für alle toString()-methode bereitstellenden objekte erreichbar ist, und einen ziemlich großen char-buffer verwaltet, und dafür sorgt, dass die einzelnen funktionen sich nicht in die quere kommen(solang's nicht zu viele sind).

    Klappt ganz gut, das lass ich auch so, ausser ihr erklärt mir, warum man sowas nicht machen darf/warum es keine elegante lösung ist 🤡

    thx @ all für die vielen ideen 👍



  • Singleton-Klasse?

    Versteh ich in dem Zusammenhang absolut nicht...
    Ganz schlechte Lösung...



  • und einen ziemlich großen char-buffer verwaltet, und dafür sorgt, dass die einzelnen funktionen sich nicht in die quere kommen(solang's nicht zu viele sind).

    AUA aua aua aua autsch weh.
    Das nicht gut.
    Auf der einen Seite kannst du zwar nix garantieren ("solang's nicht zu viele sind"), auf der anderen Seite versuchst dus trotzdem. Das kann nicht gut gehen. Das üble daran ist dass du so von Anfang an einen Fehler in dein Programm reinprogrammierst, und noch übler wird es durch die Tatsache dass "solang's nicht zu viele sind" alles zu funktionieren scheint. Wenns dann aber "zuviele sind" wird es boom machen. Und ob das in einer Art und Weise passiert dass du sofort draufkommst woran es liegt ist fraglich.

    Anders ausgedrückt: die Fehleranfälligkeit ist nicht akzeptabel solange sie nicht 0 ist.



  • so, bevor ich hier komplett zur sau gemacht werde 🤡 , zeig ich ma lieber, was ich da eigentlich gemacht habe:

    #include "UC_Singleton.hpp"
    #define MAX_BUFFER_LENGTH 5120
    
    class UC_StringBuffer:public UC_Singleton<UC_StringBuffer>
    {
    private:
            //char-buffer
    	char buffer[MAX_BUFFER_LENGTH];
    
            //offset to an empty segment
    	int emptySegmentOffset;  
    
    public:
    
    	//constructor destructor
    	UC_StringBuffer(){ emptySegmentOffset=0; }
    	~UC_StringBuffer(){}
    
    	char* GetBuffer(int bufferLength){
    
    	//required buffer is too long
    	if(bufferLength>MAX_BUFFER_LENGTH){
    		//not enough space, returning nullpointer
    		return NULL;	
    	}
    
    	//pointer to a free(?) segment of the buffer
    	char* pFreeSegment; 
    
    	if(emptySegmentOffset+bufferLength>MAX_BUFFER_LENGTH){
    		//overwrite the start of the buffer
    		pFreeSegment=&buffer[0];
    		//update the emptySegmentOffset-offset
    		emptySegmentOffset=bufferLength;
    	}else{
    		//append the segment
    		pFreeSegment=&buffer[emptySegmentOffset];
    		//update the emptySegmentOffset-offset
    		emptySegmentOffset+=bufferLength;
    	}
    
    	return pFreeSegment;
    	}
    
    };
    

    und jezt zu den genannten problemen:

    Wenns dann aber "zuviele sind" wird es boom machen.

    Eben nicht! zwar wird der buffer vollgemüllt, aber es kommt nicht zum absturz ⚠

    Und ob das in einer Art und Weise passiert dass du sofort draufkommst woran es liegt ist fraglich.

    Ich finde, es ist eigentlich einfacher, nur in dieser einen funktion nach fehlern zu suchen. Ansonsten müsste ich ja vor jedem aufruf der toString() funktion einen buffer anlegen, wo die funktion dann was reinschreiben kann. Wäre ein buffer nicht groß genug, würde alles abstürzen, und ich müsste den ganzen code nach diesem buffer absuchen. 🙄

    die Fehleranfälligkeit ist nicht akzeptabel solange sie nicht 0 ist.

    c++ programme sind doch immer bei char-arrays fehleranfällig, wenn man zu wenig speicherplatz reserviert, dann krachts halt... :p Soll ich jetzt deswegen auf java umsteigen? 🤡

    aber danke schön schonma für die konstruktive kritik 🙂 👍


Log in to reply