char mit string vergleichen



  • Hallo!

    Warum ist folgendes zulässig:

    char buffer[512];
    
    if(!strcmp(buffer, "F")
         cout << "Fehler!\n";
    

    und folgendes nicht:

    char buffer[512];
    
    if(!strcmp(buffer[0], "F")
         cout << "Fehler!\n";
    

    LG
    Philipp



  • buffer[0] ist ein einzelnes Zeichen (char), strcmp erwartet aber einen char * (= einen Zeiger auf Anfang einer Zeichenkette).



  • char buffer[512]; 
    
    if(!strcmp(&buffer[0], "F") 
         cout << "Kein Fehler!\n";
    

    funzt.
    Er prüft jetzt aber trotzdem noch den gesamten String bis zum "\0". Also das gleiche wie deine obere Funktion. Aber warum willst du das machen? Wenn du nur ein Zeichen vergleichen willst wäre nicht einfacher

    if(buffer[0] == 'F')
         cout << "Ein F, wie suess!";
    

    zu schreiben.



  • Ich würde strings grundsätzlich, auch aus Prinzip bei den trivialen Fällen wie diesen hier über std::string::operaotr== vergleichen.
    Ist doch am allereinfachsten.

    using std::string;
    char buffer[512];
    
    if (buffer == string("F");)
         cout << "Fehler!\n";
    


  • Optimizer schrieb:

    Ich würde strings grundsätzlich, auch aus Prinzip bei den trivialen Fällen wie diesen hier über std::string::operaotr== vergleichen.
    Ist doch am allereinfachsten.

    Und am langsamsten.



  • Wieso? Du glaubst doch nicht wirklich, dass der Compiler das nicht optimiert und aus dem Buffer noch aufwändig einen string konstruiert?



  • Optimizer schrieb:

    Wieso? Du glaubst doch nicht wirklich, dass der Compiler das nicht optimiert und aus dem Buffer noch aufwändig einen string konstruiert?

    Was soll er optimieren?
    string hat einen op==(char const*, string const&)
    der wird aufgerufen.

    der buffer bleibt somit ein char* - aber der string muss konstruiert werden.

    wenn er ne short-string-optimization hat, dann ist es nicht so schlimm, da der char dann nur in den statischen buffer geschrieben wird. sollte er das nicht haben, dann fordert er mit new speicher an, den er später wieder freigeben muss - das ist sehr lahm. Deshalb heisst es ja, dass man temporaries vermeiden soll.

    was spricht gegen ein
    if(!strcmp(buffer, "F"))
    ?
    oder besser
    if(buffer[0]=='F')
    ?



  • was spricht gegen ein
    if(!strcmp(buffer, "F"))
    ?

    Die Variante, die du als nächstes nennst:

    oder besser
    if(buffer[0]=='F')
    ?

    Und genau an die habe ich direkt zu beginn des Threads gedacht. 🕶



  • Wenn der string nur zum Vergleichen konstruiert wird, kann diese Konstruktion und damit das Kopieren des Buffers sehr wohl eingespart werden.
    Es ist nichts besonderes, wenn ein Compiler temp-Objekte eliminiert. Er kann auch direkt auf dem Buffer arbeiten, da er nicht schreibend darauf zugreift.



  • Was? Also den compiler, der es schaft soweit zu optimieren, den möchte ich sehen. Das grenzt ja an Zauberrei.

    Foo bar, baz;
    
    if (bar == Bla(baz))
       ...
    

    Hier soll der Compiler das erstellen eines temporären Objekts des Typs 'Bla' wegoptimieren, weil er weiß das er dur omplett anderen Code das selbe ergebnis erzielt?
    Möglich ist alles.



  • Nein, so:

    if (x == Foo(bar))   // aus x und bar lassen sich Objekte vom Typ Foo konstruieren
    // der Wert von bar ist zur Compile-Zeit bekannt.
    


  • Optimizer schrieb:

    Nein, so:

    if (x == Foo(bar))   // aus x und bar lassen sich Objekte vom Typ Foo konstruieren
    // der Wert von bar ist zur Compile-Zeit bekannt.
    

    erklärst du es mir?



  • Sei x und bar ein beliebiger (für beide der selbe) primitiver Datentyp. Unbestritten ist doch, falls die Klasse Foo den operator== definiert, der Compiler folgenden Code generiert:

    if (Foo(x) == Foo(bar))
        ;
    

    Es wird sogar für x und für bar der selbe Konstruktor aufgerufen. Glaubst du jetzt wirklich nicht, dass der Compiler ggf. das Konstruieren wegoptimieren kann?
    Vorausgesetzt natürlich, der Kontruktor ändert keine globalen Variablen oder ähnliches. Bei string wird der Kontruktor wahrscheinlich auch noch geinlined sein. Wenn der Compiler das nicht merkt, dass er beim Konstruieren lauter sinnlose Sachen (z.B. etwas kopieren, auslesen und wieder löschen) macht, dann verlier ich wirklich meinen Glauben an die Compilerhersteller. 🙄

    EDIT: Das Thema hat sich aber eh erledigt. string definiert einen

    bool __cdecl operator==(
    		const _Elem * _Left,
    		const basic_string<_Elem, _Traits, _Alloc>& _Right)
    

    Damit ist die Aussage, dass der Vergleich mit einer std::string-Konstante langsamer ist, sogar bewiesen falsch. :p

    EDIT2: Ok, so auf die Schnelle hab ich jetzt nicht feststellen können, ob Standard C++ das vorschreibt oder nur mein Compiler diesen operator mitbringt. Ich bleibe aber dabei, dass auch in allgemeinen Fällen wie oben mit der Klasse Foo der Compiler das wegoptimieren kann.



  • Optimizer schrieb:

    EDIT: Das Thema hat sich aber eh erledigt. string definiert einen

    bool __cdecl operator==(
    		const _Elem * _Left,
    		const basic_string<_Elem, _Traits, _Alloc>& _Right)
    

    Damit ist die Aussage, dass der Vergleich mit einer std::string-Konstante langsamer ist, sogar bewiesen falsch. :p

    Wenn du gelesen hättest was ich geschrieben habe, hättest du dir das sparen können.

    Es geht doch nicht darum, dass buffer konvertiert wird, sondern dass du für "f" ein string objekt konstruierst.

    Und den Compiler zeig mir mal, der das schafft, das objekt, dass du explizit erstellt hast, wegzuoptimieren (es ist _möglich_ allerdings muss der Compiler da wirklich verdammt intelligent sein)

    Wenn Programmierer verlangen, dass explizit generierte Objekte wegoptimiert werden soll, dann zweifle ich an den Programmieren.

    Denn wenn ich
    string("s")=="f"
    schreibe - dann gehöre ich sowieso geschlagen...



  • Shade schrieb:

    was spricht gegen ein
    if(!strcmp(buffer, "F"))
    ?
    oder besser
    if(buffer[0]=='F')
    ?

    die beiden Ausdrücke [edit: die if(...)-Bedingungen] liefern aber unterschiedliche Ergebnisse!

    Falls in buffer "Feldhase" steht, liefert die erste Variante false, die zweite true.



  • Der return-Wert von strcmp liefert nicht false oder true sondern bei zwei gleichen Strings null, bei ungleich -1 oder 1, je nachdem ob der String kleiner oder größer ist.



  • Shade Of Mine schrieb:

    Wenn du gelesen hättest was ich geschrieben habe, hättest du dir das sparen können.

    Stimmt, hab ich gar nicht gesehen, sorry.

    Es geht doch nicht darum, dass buffer konvertiert wird, sondern dass du für "f" ein string objekt konstruierst.

    Und worum geht es da genau, wenn "f" zur Compile-Zeit bekannt ist?

    Und den Compiler zeig mir mal, der das schafft, das objekt, dass du explizit erstellt hast, wegzuoptimieren (es ist _möglich_ allerdings muss der Compiler da wirklich verdammt intelligent sein)

    Ich denke, dass Compiler das auch schaffen können, wenn der Wert zur Compile-Zeit nicht bekannt ist. Wo ist denn das Problem? Der Compiler wird doch merken, dass er nen buffer kopiert, nur ausliest und gleich wieder löscht.

    Denn wenn ich
    string("s")=="f"
    schreibe - dann gehöre ich sowieso geschlagen...

    Darum gehts ja nicht. Es geht um <unbekannter Wert im Buffer> == string("s").



  • Optimizer schrieb:

    Ich denke, dass Compiler das auch schaffen können, wenn der Wert zur Compile-Zeit nicht bekannt ist. Wo ist denn das Problem? Der Compiler wird doch merken, dass er nen buffer kopiert, nur ausliest und gleich wieder löscht.

    Das würde bedeuten, dass der Compiler die string klasse sehr gut kennst.
    er muss dazu wissen, was strlen("f") liefert. dazu muss strlen() intrinsic sein. sollte "f" zur compiletime nicht feststehen kann er sowieso nix machen.

    ich gehe mal davon aus, dass string einfach implementiert ist - ohne besondere optimierung (zB nicht als char* sondern als eine art list oder so) - denn sonst wäre die optimierung gänzlich unmöglich.

    betrachten wir den ctor:

    string::string(char const* s)
    {
      length=strlen(s);
      if(length<STATIC_BUFFER_SIZE)
      {
        use_static_buffer=true;
        strcpy(static_buffer, s);
      }
      else
      {
        use_static_buffer=false;
        buffer=new char[length+1];
        strcpy(buffer, s);
      }
    }
    

    Das wegzuoptimieren ist schon ne ordentliche leistung.
    er muss strlen(s) auswerten und erkennen, dass dann immer der eine if zweig ausgeführt wird.

    dann der op==

    bool operator==(char const* s1, string const& s2)
    {
      return !strcmp(s1, s2.c_str());
    }
    

    dazu müssen wir c_str kennen:

    char const* c_str() const
    {
      if(use_static_buffer) return static_buffer;
      else return buffer;
    }
    

    Jetzt muss der compiler wissen, dass wenn strlen("f") kleiner als static_buffer_size ist, c_str() static_buffer returned und static_buffer eine kopie von "f" ist.

    möglich ist es - aber ich bezweifle, dass es ein compiler macht. er müsste in soviele funktionen gleichzeit sehen um das optimieren zu können...

    wäre geil wenn er es könnte - aber solche optimierungen vorauszusetzen ist n bisschen gewagt, zumal noch nichtmal alle compiler NRVO und ähnliches gängige nicht können.

    Denn wenn ich
    string("s")=="f"
    schreibe - dann gehöre ich sowieso geschlagen...

    Darum gehts ja nicht. Es geht um <unbekannter Wert im Buffer> == string("s").

    string("s") == buffer;
    ist genauso bescheuert.



  • Du darfst ja nicht vergessen, dass der ganze Geiz höchstwahrscheinlich geinlined wird. Das erleichtert das Optimieren enorm, weil alles direkt an einer Stelle steht und der Compiler so leicht erkennen kann, was unnötig ist.
    Natürlich kann man nicht einfach davon ausgehen, dass ein Compiler das macht. Aber ich rechne da schon mit einer einigermaßen hohen Wahrscheinlichkeit. An erster Stelle steht sowieso die Wartung des Codes. Wenn das eine kritische Stelle ist, kann ich dann immer noch Messungen durchführen.

    string("s") == buffer;
    ist genauso bescheuert.

    Wieso? Finde ich die beste Art, einen C-String mit einem Wert zu vergleichen. Und vor allem wesentlich besser als strcmp(). Mach doch mal bitte nen anderen Vorschlag, vielleicht lern ich was. 🙂

    Achja und bei nem konstanten Vergleichswert, glaub ich sowieso nicht, dass der Compiler den std::string jedesmal neu konstruiert.



  • Optimizer schrieb:

    Du darfst ja nicht vergessen, dass der ganze Geiz höchstwahrscheinlich geinlined wird. Das erleichtert das Optimieren enorm, weil alles direkt an einer Stelle steht und der Compiler so leicht erkennen kann, was unnötig ist.
    Natürlich kann man nicht einfach davon ausgehen, dass ein Compiler das macht. Aber ich rechne da schon mit einer einigermaßen hohen Wahrscheinlichkeit. An erster Stelle steht sowieso die Wartung des Codes. Wenn das eine kritische Stelle ist, kann ich dann immer noch Messungen durchführen.

    Sagmal, liest du das was ich schreibe?

    Ich bin von einer ganz simplen implementierung ausgegangen - die es so nie geben wird. Das ganze ist voll mit anderen sachen, zB teilt sich static_buffer und buffer den selben speicher. uU wird das 0 am ende des strings nicht gespeichert, etc.

    Das sind situationen wo das wegoptimieren wesentlich komplexer bis unmöglich wird.

    denke zB an COW beim VC++ - damit gibt es _garantiert_ dynamisch angeforderten speicher und das ganze wird um eine ebene tiefer versetzt (also mehr funktionen und mehr code == wesentlich komplexer)

    string("s") == buffer;
    ist genauso bescheuert.

    Wieso? Finde ich die beste Art, einen C-String mit einem Wert zu vergleichen. Und vor allem wesentlich besser als strcmp(). Mach doch mal bitte nen anderen Vorschlag, vielleicht lern ich was. 🙂

    strcmp("s", buffer); - was ist daran schlecht?
    Da ist genau der Grund warum es strcmp gibt - für exakt diese Situation.

    Achja und bei nem konstanten Vergleichswert, glaub ich sowieso nicht, dass der Compiler den std::string jedesmal neu konstruiert.

    neu wahrscheinlich nicht, aber es wird einmal konstruiert - das ist ein overhead. Ein _sinnloser_ overhead. es bringt dir garkeine vorteile string("s") zu schreiben.

    string("s") == buffer
    sieht einfach komisch aus - und ich frage mich sofort: warum string("s")? warum ein string objekt erstellen?
    bei strcmp("s", buffer) ist mir alles klar.

    ich schreibe ja auch

    strlen(s)
    und nicht
    string(s).size()


Anmelden zum Antworten