Problem mit Stringrückgabe



  • 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'



  • Original erstellt von Shade Of Mine:
    **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)
    **

    Ach, du hast gar nicht allgemein gesprochen? Wenn wir bei dem Spezialfall bleiben, dass der Aufrufer entweder weiß, wieviel Speicher das Ergebnis von foo belegt, oder er den Worst-Case abschätzen kann (und dieser Wert nicht völlig aus dem Rahmen fällt), dann hast du Recht. Bei dieser \ -> \\ Funktion bräuchte man nichtmal durchzählen, im Worst-Case belegt das Ergebnis nur doppelt soviel Speicher wie der Ausgangsstring, das ist durchaus tragbar.

    Ich bezog mich auf die IMHO allgemeine Aussage, Rückgabewerte, die der Aufrufer selbst zu beseitigen habe, seien schlechtes C.

    **
    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):
    **

    Es ist IMHO nicht generell ratsam, sich an 30 Jahre alte Designs zu halten. Damals waren auch statische Puffer, gets und andere Grausamkeiten en-vogue.

    Aber ich glaube, wir können uns auf einen gemeinsamen Standpunkt einigen: Wenn der Aufrufer ohne großen Aufwand den Speicher für das Funktionsergebnis bereitstellen kann, soll er das tun. Ansonsten tut die Funktion das und überläßt dem Aufrufer, später eine Aufräum-Funktion zu benutzen (sei es free oder delete_complicated_structure).



  • Original erstellt von Bashar:
    Aber ich glaube, wir können uns auf einen gemeinsamen Standpunkt einigen: Wenn der Aufrufer ohne großen Aufwand den Speicher für das Funktionsergebnis bereitstellen kann, soll er das tun. Ansonsten tut die Funktion das und überläßt dem Aufrufer, später eine Aufräum-Funktion zu benutzen (sei es free oder delete_complicated_structure).

    perfekt!
    damit bin ich glücklich!

    wobei ich mich doch gegen free ausspreche (auch wenn delete_compilicated_structure(void* p) nur ein free(p) macht ;))

    aber darüber zu diskutieren ist IMHO jetzt OT


Anmelden zum Antworten