Stringvariable mit String-Literal vergleichen



  • Hallo,
    unser Prof. hat uns darauf aufmerksam gemacht, dass das Verhalten beim Referenzvergleich mit String-Literalen nicht definiert ist. Gemeint ist, wie sich der Compiler verhalten soll, wenn zwei wertgleiche Strings erstellt werden. Soll der Compiler hier auf dieselbe Referenz verweisen, um Speicherplatz zu sparen (wie in Java) oder evtl. einen String erstellen? Aber was hat das mit dem Referenzvergleich zu tun? Das sind m. A. n. zwei paar Schuhe.
    Dennoch: Der GCC 4.6.1 gibt auch tatsächlich eine Warnung aus:

    warning: comparison with string literal results in unspecified behavior

    Das Komische ist jedoch, dass wenn ich ein (char*) vor dem String-Literal schreibe, die Warnung verschwindet. Mein Prof. war da auch verblüfft und meinte, dass das eigentlich das Gleiche sei und der Compiler hier möglicherweise schlampt.

    Habt ihr vielleicht eine andere Erklärung?

    int main()
    {
        char *a = "Hallo";
    
        printf("%d\n", a == "Hallo"); /* Erzeugt Warnung! */
        printf("%d\n", a == (char*) "Hallo"); /* Keine Warnung! */
    
        return 0;
    }
    

    Danke im Voraus!

    L. G.
    Steffo



  • Ich würde sagen dass durch den cast erst ein neuer char* konstruiert wird (verschwindet wahrscheinlich nach Optimierung), bei dem sich der compiler nicht mehr daran erinnert, dass er auf ein string literal zeigt, ergo keine warning.



  • Steffo schrieb:

    Hallo,
    unser Prof. hat uns darauf aufmerksam gemacht, dass das Verhalten beim Referenzvergleich mit String-Literalen nicht definiert ist.

    Das Wort "Referenzvergleich" ist hier in C++ unpassend. Was in Java "Referenz" heißt, heißt in C++ "Zeiger". Und das ist auch das, was hier passiert. Du vergleichst zwei Adressen. Eine Adresse steht in der Zeigervariable a drin und die andere Adresse ergibt sich aus dem "array-to-pointer-decay" für das String-Literal.

    Steffo schrieb:

    Gemeint ist, wie sich der Compiler verhalten soll, wenn zwei wertgleiche Strings erstellt werden. Soll der Compiler hier auf dieselbe Referenz verweisen, um Speicherplatz zu sparen (wie in Java) oder evtl. einen String erstellen?

    Vorsicht mit den Fachbegriffen. Auf "Referenzen verweist" man nicht. Referenzen sind keine Objekte, auf die man verweisen kann. War das jetzt eine Frage?

    Steffo schrieb:

    Aber was hat das mit dem Referenzvergleich zu tun? Das sind m. A. n. zwei paar Schuhe.

    Scheint, als hättest Du Stringliterale noch nicht 100%ig verstanden. Ein Stringliteral ist ein Ausdruck, welcher sich auf eine im statischen Speicher liegende, nullterminierte Zeichenkette bezieht (konstantes char-Feld). Wenn jetzt "Hallo" mehrfach im Code auftaucht, darf der Compiler diese Zeichenkette nur ein einziges Mal im Kompilat (in der Objektdatei) ablegen wobei jede Stelle, in der dieses "Hallo" steht, sich auf dasselbe Feld beziehen würde. Ob ein Compiler so etwas macht, ist aber nicht festgelegt. Es wird ihm aber erlaubt. Dementsprechend kann bei diesem Vergleich sowohl true als auch false rauskommen, je nachdem, ob das zweite "Hallo" sich auf dasselbe Feld bezieht.

    Steffo schrieb:

    Dennoch: Der GCC 4.6.1 gibt auch tatsächlich eine Warnung aus:

    warning: comparison with string literal results in unspecified behavior

    Das Komische ist jedoch, dass wenn ich ein (char*) vor dem String-Literal schreibe, die Warnung verschwindet. Mein Prof. war da auch verblüfft und meinte, dass das eigentlich das Gleiche sei und der Compiler hier möglicherweise schlampt.

    Seit wann darf man sich auf das Fehlen von Warnungen verlassen? Ein (char*) davor ändert nichts am Verhalten des Programms (es ist immer noch unspezifiziert). Es sieht nur so aus, als ob die Warnregel im Compiler so formuliert ist, dass die Warnung nur kommt, wenn == direkt auf ein Stringliterak angewendet wird. Mit dem (char*) davor ist die rechte Seite des == Operators kein Stringliteral mehr sondern die Adresse des ersten Zeichens. Diese Konvertierung passiert im ersten Fall aber auch, nur implizit.

    Steffo schrieb:

    Habt ihr vielleicht eine andere Erklärung?

    Ich weiß nicht, was Du hier erwartest. Wann ein bestimmter Compiler es für nötig hält, vor etwas zu warnen und wann nicht, ist Sache des Herstellers. So'was ist nicht im C++ Standard festgehalten.



  • String-Literale speichert man außerdem sowieso in nem char const*.


  • Mod

    314159265358979 schrieb:

    String-Literale speichert man außerdem sowieso in nem char const*.

    Nein. In einem char const* speichert man Zeiger auf String-Literale.



  • krümelkacker schrieb:

    Seit wann darf man sich auf das Fehlen von Warnungen verlassen? Ein (char*) davor ändert nichts am Verhalten des Programms (es ist immer noch unspezifiziert).

    Deswegen war ich ja verwirrt.

    Ich weiß nicht, was Du hier erwartest. Wann ein bestimmter Compiler es für nötig hält, vor etwas zu warnen und wann nicht, ist Sache des Herstellers. So'was ist nicht im C++ Standard festgehalten.

    Ich hatte erwartet, dass sich der Compiler durchgehend konsistent verhält.

    L. G.
    Steffo



  • SeppJ schrieb:

    314159265358979 schrieb:

    String-Literale speichert man außerdem sowieso in nem char const*.

    Nein. In einem char const* speichert man Zeiger auf String-Literale.

    Du weißt, dass ich das meinte 🙂

    Edit: Und wenn man ganz genau ist, speichert man die Addresse eines String-Literals.


Anmelden zum Antworten