Problem mit Stringrückgabe



  • *grummel* mensch is ja kagge.
    dann könnte ich die funktion ja gar nicht woanders benutzen.
    weil dann müsste ich ja merken dass ich immer wenn ich diese funktion benutze ein free dahinter machen muss 😞
    kann man das nicht auch anders lösen?

    ausserdem:

    wenn ich das im main mache kommt: error C2065: 'outputstring' : nichtdeklarierter Bezeichner 😉

    [ Dieser Beitrag wurde am 22.04.2003 um 23:03 Uhr von Peter Piksa editiert. ]



  • Original erstellt von TriPhoenix:
    Warum sollte er. Damit riskiert man höchstens, dass alter Sourcecode der eventuell mtii C99 nicht mehr läuft (kenne den Standard leider nicht genau genug dafür) nicht mehr läuft, denn der Compielr heißt ja nach wie vor gcc. Deswegen hat man lieber einen Schalter dafür geschaffen.

    ihmo ist C99 voll abwaertskompatibel. das sollte also kein grund sein.



  • Original erstellt von Peter Piksa:
    ***grummel* mensch is ja kagge.
    dann könnte ich die funktion ja gar nicht woanders benutzen.
    weil dann müsste ich ja merken dass ich immer wenn ich diese funktion benutze ein free dahinter machen muss 😞
    kann man das nicht auch anders lösen?
    **

    Wahlweise mit ner globalen Variable oder einer eigenen Zeigerverwaltung die sich drum kümmert 🙂

    **
    wenn ich das im main mache kommt: error C2065: 'outputstring' : nichtdeklarierter Bezeichner 😉
    **

    Klar, du musst es ja auch zwischenspeichern. Also wie ich oben gepostet hatte.



  • Original erstellt von TriPhoenix:
    Wahlweise mit ner globalen Variable oder einer eigenen Zeigerverwaltung die sich drum kümmert 🙂

    *staun* 😮 was istn das? *wissenwill*

    das zwischenspeichern ist doch in der funktion selbst.
    und in der funktion kann ich ja nicht free machen.
    und im main nicht weils da unbekannt ist.

    [ Dieser Beitrag wurde am 22.04.2003 um 23:22 Uhr von Peter Piksa editiert. ]



  • Original erstellt von Peter Piksa:
    ***staun* 😮 was istn das? *wissenwill*
    **

    Naja, man kann sogenannte SmartPointer schreiben, Zeiger die sich selbst freigeben wenn sie nicht mehr gebraucht werden. Oder eine Funktion die jedes malloc notiert und bei nicht mehr gebruachten Werten irgendwie die Dinge wieder freigibt. Ist aber alles ein bisschen Overkill für das was du willst 🙂

    **
    das zwischenspeichern ist doch in der funktion selbst.
    und in der funktion kann ich ja nicht free machen.
    und im main nicht weils da unbekannt ist.
    **

    Nein das zwischenspeichern soll gerade in der main sein. Die Main-Funktion soll so aussehen:

    int main()
    {
        char *rueckgabe = strrplc("C:\\Mp3\\song.mp3");
        cout << rueckgabe << endl;
        free(rueckgabe);
        return (0);
    }
    

    PS: ich habe inzwischen gesehen, dass spitzenbleistift in seiner GCC-frage auch noch ne nette Variante gebaut hat.



  • Jetzt versteh ich aber auch nicht mehr wirklich was... 😞
    Bin grade bei diesem code hier.
    der macht keine fehler beim linken/compilieren aber wenn man das prog ausführt, kratzt er ab....

    #include <iostream.h>
    #include <string.h>
    #include <stdlib.h>
    
    char * strrplc(char * string)
    {
        int i, a;
        int lenght = strlen(string);
        int slashcount = 0;
    
        for(i = 0 ; i<=lenght ; i++)
        {
            if (string[i] == 92)
            slashcount++;
        }
        char *outputstring = (char*)malloc(sizeof(char)*slashcount);
        for (a=0, i=0; i<=lenght ; i++, a++)
        {
            if (string[i] != 92)
                outputstring[a] = string[i];
            else
            {
                outputstring[a] = 92;
                outputstring[a+1] = 92;
                a++;
            }
        }
        cout << "Der Outputstring in der Funktion:\t" << outputstring << endl;
        return(outputstring);
    
    }
    
    int main()
    {
        char *outputstring = strrplc("C:\\Mp3\\song.mp3");
        cout << outputstring<<endl;
        free(outputstring);
        return (0); 
    }
    


  • buffer overrun



  • Du musst natürlich per malloc soviel Platz alloziieren wie du brauchst, also strlen(string) + 1 (länge + abschließendes 0-Byte)



  • HILFE!!!

    was ist denn hier los??
    Kann keiner C programmieren...?

    globale variablen, caller muss free'en,...
    das ist doch kein C

    [cpp]
    char * strrplc(const char * string**, char* outputstring**)
    {
    int lenght = strlen(string);
    for (int i=0, a=0; i<=lenght ; i++, a++)
    {
    if (string[i] != 92) //92? was ist 92??
    outputstring[a] = string[i];
    else
    {
    outputstring[a] = 92;
    outputstring[a+1] = 92;
    a++;
    }
    }
    cout << "Der Outputstring in der Funktion:\t" << outputstring << endl;
    return(outputstring);
    }[/cpp]

    der caller muss den speicher besorgen!

    92? was ist 92? absolut unleserlich.
    und i<=length
    das ist ja wohl n scherz, oder?
    da macht man < und NUL't den string nachher explizit

    ausserdem: ich würde ordentlich C++ programmieren, anstatt so einen mischmasch... schaut ja gräßlich aus



  • Original erstellt von Shade Of Mine:
    **
    92? was ist 92? absolut unleserlich.
    **

    ja, ich fand das programm auch ziemlich schlimm. (j waere imho auch besser als a)

    aber anstatt 92 kann man ja nicht wie ueblich '\' schreiben.
    also '\' oder:
    #define bslash 92

    ausserdem sollten die '{' nicht in ner eigenen zeile, sondern in der des befehls stehen :p



  • Original erstellt von Shade Of Mine:
    **caller muss free'en,...
    das ist doch kein C
    **

    Kann es sein dass du gerade C und C++ verwechselst? In C gibts keine Destruktoren, die magisch die Arbeit tun, also kommst du nicht umhin, das zerstören dynamischer Datenstrukturen dem Aufrufer zu überlassen. Das kann sowas wie delete_linked_list(mylist); sein, oder bei char-Puffern eben free(buffer).

    Wenn der Aufrufer den Speicher besorgen muß, dann muß er auch wissen, wie groß der Puffer sein soll. Das geht aber nicht immer.



  • Original erstellt von Shade Of Mine:
    **HILFE!!!

    was ist denn hier los??
    Kann keiner C programmieren...?

    globale variablen, caller muss free'en,...
    das ist doch kein C
    **

    Ich seh das Problem nicht. Globale Variablen sind antürlich wie gesagt unschön aber viele Runtime-Libraries arbeiten auch problemlos damit, wenn man sie vernünftig behandelt. Und Funktion-alloziiert-caller-freet, so arbeiten auch einige Runtime-Library-Funktionen und OS-Calls. Das Problem seh ich wirklich nicht.



  • Minsch, was meckert ihr denn auf einmal alle so viel rum?
    92 ist einfach nur der ASCII Wert für einen Backslash, das wird ja wohl nicht so schwer sein 😉

    Und Shade of Mine, was bringt mir dass nun wenn ich als zweiten Parameter in meiner Funktion ein "char* outputsring" mache? dadurch ist das problem leider immernoch nicht gelöst....



  • Original erstellt von Peter Piksa:
    Und Shade of Mine, was bringt mir dass nun wenn ich als zweiten Parameter in meiner Funktion ein "char outputsring" mache? dadurch ist das problem leider immernoch nicht gelöst....*

    Das verlagert das Problem. Dann muss sich die main drum kümmern entweder per malloc oder per lokaler Variable Speicherplatz bereitzustellen, in den die Funktion dann den Inhalt reinpacken kann.



  • @TriPhoenix: Könntest du mir das mal an einem passendem Codebeispiel erklären? Ich komm nicht ganz auf die Logik von diesem Gebilde klar? 😕



  • Original erstellt von Bashar:
    Kann es sein dass du gerade C und C++ verwechselst?

    nein, ich hab mich nur ungut ausgedrückt:
    ein
    char* s=foo();
    free(s);
    ist furchtbar!

    man erkennt die zusammengehörenden mallocs und frees nicht!
    das ist IMHO böse.

    da ist ein

    char* s=malloc(100);
    foo(s);
    free(s);

    wieviel besser.

    @TriPhoenix:
    sicher kannst du auch globale variablen verwenden, wenn du willst.
    aber irgendwann fällt dir das auf den kopf...

    sag mir mal eine gute Library die globale variablen BRAUCHT (nicht HAT)
    errno ist IMHO ein Grenzfall. Hier könnte man natürlich getter und setter schreiben, aber errno soll ja gerade überall zugänglich sein - ich wüsste nicht wie man das anders machen könnte.

    aber sonst? willst du für jede Funktion eine globale variablen nehmen? solche situationen wie diese gibt es wie sand am mehr!

    Beispiel strcpy:
    strcpy zeigt wie der C weg ist dieses Problem zu lösen.
    Es ist nur natürlich, wenn man sein Design dem der Sprache anpasst.

    übrigens muss ich mich entschuldigen!
    spitzenbleistift hat bereits die richtige Lösung gesagt, aber ihr seid ja nichteinmal darauf eingegangen 😞

    Was spricht gegen die variante mit dem übergeben des Buffers? (jede vernüftige Library macht das so - ganz ohne globale variablen!)

    Und man muss keine Doku lesen um zu verstehen was abgeht:
    denn wenn die funktion ein malloc macht, dann ist das nicht nur unnatürlich, sondern kann vielleicht auch sinnlos langsam sein (weil ich als caller weiß, dass der string locker auf den stack passt)

    und woher soll ich jetzt wissen ob ich free() machen muss?

    was ist, wenn ich einen GC verwende?
    muss ich dann gc_free() oder free() verwenden?

    was wenn ich ne gute speicherverwaltung habe - my_malloc holt den speicher aus einem vorreservierten pool, dann würde die funktion nur das normale malloc verwenden und wäre langsamer und ich hätte wieder das free() problem.

    oder was wenn ich memory leaks aufzeichnen will und deshalb
    #define malloc my_malloc
    #define free my_free
    gemacht habe?
    dann kann ich den speicher ja garnichtmal freigeben!

    man braucht hier keine globalen variablen (ich wüsste nichteinmal wie man welche verwenden könnte) und smart pointer braucht man auch nicht.

    wo liegt das problem bei der normalen variante??

    zum ursprünglichen problem:
    der caller reserviert genügend speicher für outputstring und ruft die funktion dann so auf:

    char str[100]; //kein langsames malloc nötig!!!
    printf("%s",strrplc("C:\\was\\weiss.ich",str));
    

    strrplc kann sich sicher sein, dass in outputstring genug platz ist (das garantiert der caller) und kann ganz ohne speicherverwaltung auskommen!

    und niemand wird ein free() vergessen...

    vergleicht mal:

    //variante 1
    printf("%s",strrplc("C:\\was\\weiss.ich"));
    //wer vermisst hier ein free() ??
    
    //variante 2
    strrplc("C:\\was\\weiss.ich");
    printf("%s",strrplc_var); //globale variable
    //wo kommt die denn her??
    
    //variante 3
    char* s=malloc(100);
    strrplc("C:\\was\\weiss.ich",s);
    printf("%s",s);
    //hier fehlt n free() - das malloc ist ja noch zu sehen
    

    [ Dieser Beitrag wurde am 23.04.2003 um 14:06 Uhr von Shade Of Mine editiert. ]



  • Hmm Ich verstehe zwar nicht alles was du sagst Shade of Mine, aber es hört sich vernünftig an. 🙂 Du weisst schon was du schreibst *dirvertraut* 🙂

    Das ist mir übrigends echt irgendwie nicht bewusst geworden mit der Respond vom Spitzenbleistift. Sein Code funzt prima. aber ich habe da noch eine frage. (siehe comment im code)

    #include <stdio.h>
    #include <string.h>
    
    char *strrplc(char *string, char *outputstring)
    {
        int lenght = strlen(string), a, i;
        for(i=0, a=0; i<=lenght ; i++, a++)
        {
            if (string[i] != 92)
                outputstring[a] = string[i];
            else
            {
                outputstring[a] = 92;
                outputstring[a+1] = 92;
                a++;
            }
        }
        printf("Der Outputstring in der Funktion:\t %s\n",outputstring);
        return (outputstring);
    }
    
    int main(void)
    {
        char outputstring[150]; // das ist nicht so toll weil wenn der output länger ist als 130 schmiert es ab. kann man das nicht irgendwie dynamisch oder so machen? 
        printf("%s\n",strrplc("C:\\Mp3\\song.mp3", outputstring));
        return (0);
    }
    


  • Original erstellt von Shade Of Mine:
    *nein, ich hab mich nur ungut ausgedrückt:
    ein
    char
    s=foo();
    free(s);
    ist furchtbar!

    man erkennt die zusammengehörenden mallocs und frees nicht!
    das ist IMHO böse.

    da ist ein

    char* s=malloc(100);
    foo(s);
    free(s);

    wieviel besser.
    **

    Geh doch auf das Argument ein, dass nur foo weiß, wie groß der Buffer sein soll, anstatt das gesagte zu widerholen.





  • warum sollte nur foo wissen wir groß der buffer zu seien hat?
    der caller weiß das doch eigentlich besser...

    so wie ich diese funktion verstanden habe, macht sie aus \ ein \\
    nun weiß der caller aber was das für ein string ist (zumindest in vielen fällen)

    nehmen wir nun an, dass der caller es nicht weiß.
    nehmen wir nun an, dass die funktion selber speicher besorgt:

    die funktion muss einmal den string durchlaufen um zu wissen wie groß der speicher sein muss.
    dann wird der speicher reserviert.
    dann wird nochmal durchgelaufen und alle sachen ersetzt.

    nun nehmen wir aber an, dass der caller immer noch nicht weiß, was das für ein string ist -> aber er selber den speicher besorgen muss:
    er muss den string durchlaufen um den speicher zu reservieren.
    dann wird die funktion aufgerufen
    dann wird ersetzt

    gleiche arbeitsschritte.

    nun nehmen wir aber an, dass der caller den string bereits kennt.
    dann entfällt der punkt mit dem durchlaufen des strings und es wird nur ersetzt!

    also pseudo code:

    Variante 1:
    call foo(str)
    
    function foo(str)
    {
      ermittele benötige länge von str
      reserviere speicher für buffer
      ersetze in str und speichere in buffer
      return buffer
    }
    
    Variante 2:
    ermittele benötige länge von str (kann entfallen)
    reserviere speicher für buffer
    
    call foo(str)
    
    function foo(str)
    {
      ersetze in str und speichere in buffer
      return buffer
    }
    

    Vergleichen wir beide varianten:

    Variante 1:
    + buffer overflows unmöglich
    - unintuitiv (free wird uU vergessen)

    Variante 2:
    + möglicherweise schneller
    + intuitiv (free wird wahrscheinlich nicht vergessen)
    - buffer overflows möglich

    weiters gibt es noch zu sagen:
    jede funktion soll immer exakt EINE aufgabe haben.

    Bei variante 1 ist die Aufgabe von foo:
    "reserviere genpgend Bufferspeicher und erstze \ durch \"
    bei Variante 2:
    "ersetze \ durch \"

    performance problem bei Variante 1 bei folgender situation:
    [cpp]
    for(int i=0;i<anzahl;++i)
    {
    char* temp=foo(array[i]);
    mach_was_wichtiges(temp);
    free(temp);
    }[/code]

    das problem gibt es bei Variante 2 nicht:
    [cpp]
    {
    char temp[big_enough];
    for(int i=0;i<anzahl;++i)
    {
    foo(array[i],temp);
    mach_was_wichtiges(temp);
    }
    }[/code]

    gehen wir nun das buffer overflow problem an, denn das ist ein pluspunkt für variante 1:

    wir schauen uns dazu die str* fuktionen der C Standard Library an (denn, wie bereits erwähnt, halte ich mich gerne an das design der sprache):
    woher weiß ich bei
    strcpy(trg,src);
    dass trg groß genug ist?

    ganz einfach, ich kann ja:

    if(strlen(src)>=memory_of_trg)
    {
    trg=realloc(trg,strlen(src));
    }
    strcpy(trg,src);

    machen und ein buffer overflow ist verhindert.

    warum nicht auch bei uns?
    wir schreiben eine funktion foolen die die benötigte länge bestimmt:

    [cpp]
    int len=1;
    while(*str)
    {
    if(*str=='\') ++len;
    ++len;
    ++str;
    }

    jetzt kann der caller bestimmen, ob er
    [code type="c++"]
    char buffer[may_too_little];
    foo("...",buffer);
    

    oder ob er
    [cpp]
    char* buffer=malloc(foolen("...");
    foo("...",buffer);[/code]
    macht.

    Ich denke dass es uU sinnvoll seien kann foolen() aufrzurfen, aber in den meisten fällen wird es wohl unnötig sein (wie bei strcpy)

    der sicherheit halber kann man uU auch ein foon schreiben, welches nur maximal n zeichen in den buffer schreibt (allerdings halte ich dies für übertrieben wenn wir bereits ein foolen() haben)

    und wer lieber will dass foo den speicher reserviert, der macht ein
    [cpp]
    char* savefoo(char* str)
    {
    char* temp=malloc(foolen(str));
    foo(str,temp);
    return temp;
    }[/code]

    warum man dies nicht machen sollte habe ich ja schon erklärt, aber wenn das jemand will - bitte.

    aber man kann weiterhin foo() aufrufen ohne den overhead des 'größe berechnens'


Anmelden zum Antworten