Illegal?



  • Hi!

    void fill_string ( char* s, int n )
    {
    	memset ( s, 'a', n );
    }
    
    int main() 
    { 
    	string str;
    	int n = 10;
    	str.resize(n);
    	fill_string( &str[0], 10 );
    	cout << str << endl;
    
    	return 0;
    }
    

    Hab ne Frage zur Adressierung eines string. Speziell geht es mir um dieses Fützelchen:

    &str[0]

    Ist das legaler C++ Code?

    Ich möchte nämlich für eine UTF-8 -> UTF-16LE Konversion die Windowsfunktion MultiByteToWideChar benutzen, und meine Strings auf diese Weise als Parameter übergeben.

    LG.



  • funzt auch so:

    void fill_string ( const char* s, int n )
    {
    	memset ( (void*)s, 'a', n );
    }
    
    int main() 
    { 
    	string str;
    	int n = 10;
    	str.resize(n);
    	fill_string( str.c_str(), 10 );
    	cout << str << endl;
    

    aber ob das legal ist, k.a.



  • legal/illegal schei... schrieb:

    Hab ne Frage zur Adressierung eines string. Speziell geht es mir um dieses Fützelchen:

    &str[0]

    Ist das legaler C++ Code?

    Soviel ich weiss, garantiert der Standard nicht (zumindest nicht explizit wie bei std::vector ), dass std::string intern ein Array speichern muss und man per &str[0] darauf zugreifen kann. Von daher würde ich mich nicht unbedingt darauf verlassen. Du kannst ja direkt über std::string::operator[] auf die einzelnen Zeichen zugreifen. Oder std::fill() verwenden. Oder std::string::assign() . Oder...

    funz0r schrieb:

    aber ob das legal ist, k.a.

    Auf gar keinen Fall! Alleine das versteckte Wegcasten der Constness ist böse. Und dann noch memset() auf einen Speicher anwenden, bei dem du gar nicht weisst, wem er gehört oder wie lange er existiert. Mach das nicht, es gibt wie erwähnt bessere Möglichkeiten.



  • Nexus schrieb:

    Du kannst ja direkt über std::string::operator[] auf die einzelnen Zeichen zugreifen. Oder std::fill() verwenden. Oder std::string::assign() . Oder...

    Das Füllen übernimmt die Windowsfunktion MultiByteToWideChar. Ich dachte mir, ich könnte so ein bisschen Code sparen, wenn ich nicht über dynamische char* gehe.



  • Hm.
    Ist bei std::string denn überhaupt garantiert, dass &str[0] einen Zeiger auf eine Array-Repräsentation des Strings liefert? Soweit ich weiss nicht.
    Genau dafür gibt es nämlich die Memberfunktion c_str . (Die dann allerdings nur nen const-Zeiger zurückgibt.)

    Alleine das versteckte Wegcasten der Constness ist böse

    Welches const?
    Siehe: http://www.cplusplus.com/reference/string/string/operator[]/

    Man darf sogar ganz offiziell Änderungen über den operator [] machen. Bloss fehlt bei std::string eben die Garantie, wie es sie bei std::vector gibt, dass &str[0] der Anfang eines zusammenhängenden Speicherbereichs ist, wo alle Elemente hintereinander abgelegt sind.
    Etwas formaler: die Garantie dass (&str[N]) == (&str[0]) + N



  • l.i.s schrieb:

    Nexus schrieb:

    Du kannst ja direkt über std::string::operator[] auf die einzelnen Zeichen zugreifen. Oder std::fill() verwenden. Oder std::string::assign() . Oder...

    Das Füllen übernimmt die Windowsfunktion MultiByteToWideChar. Ich dachte mir, ich könnte so ein bisschen Code sparen, wenn ich nicht über dynamische char* gehe.

    Verwende einen std::vector<char> (bzw. wchar_t) als Puffer, und mach dann so nen String draus:

    std::wstring lala()
    {
        std::vector<wchar_t> buffer;
        buffer.resize(initial_size);
    
        // ... das ganze MultiByteToWideChar gedöns ...
    
        return std::wstring(buffer.begin(), buffer.begin() + result_length);
    }
    


  • hustbaer schrieb:

    Bloss fehlt bei std::string eben die Garantie, wie es sie bei std::vector gibt, dass &str[0] der Anfang eines zusammenhängenden Speicherbereichs ist, wo alle Elemente hintereinander abgelegt sind.
    Etwas formaler: die Garantie dass (&str[N]) == (&str[0]) + N

    oh! bedeutet also, das auch wenn ich c_str als parameter übergebe, nicht sicher sein kann, das alle elemente hintereinander liegen?
    hmm... dann sollte ich wohl lieber doch ein char* array zwischenschalten`?



  • hustbaer schrieb:

    Verwende einen std::vector<char> (bzw. wchar_t) als Puffer, und mach

    ah, ok. ist zwar auch ne ganz schöne konvertierungsorgie, aber vermutlich der sicherste weg.



  • hustbaer schrieb:

    Alleine das versteckte Wegcasten der Constness ist böse

    Welches const?

    Das hier, wie gesagt versteckt:

    //                 const
    //                   |
    //                   v
    void fill_string ( const char* s, int n )
    {
    //       versteckter const_cast!
    //              |
    //              v
    	memset ( (void*)s, 'a', n );
    }
    


  • string bzw wstring ist doch sowieso nur ein Container wie vector für char/wchar_t mit etwas erweiterten Möglichkeiten. Natürlich geht das.



  • Nexus schrieb:

    hustbaer schrieb:

    Alleine das versteckte Wegcasten der Constness ist böse

    Welches const?

    Das hier, wie gesagt versteckt:

    //                 const
    //                   |
    //                   v
    void fill_string ( const char* s, int n )
    {
    //       versteckter const_cast!
    //              |
    //              v
    	memset ( (void*)s, 'a', n );
    }
    

    oops, sorry 🙂
    hab nur im kopfposting geguckt.

    p.S.: ja, die C-style casts sind die schlimmsten!



  • l.i.s schrieb:

    hustbaer schrieb:

    Bloss fehlt bei std::string eben die Garantie, wie es sie bei std::vector gibt, dass &str[0] der Anfang eines zusammenhängenden Speicherbereichs ist, wo alle Elemente hintereinander abgelegt sind.
    Etwas formaler: die Garantie dass (&str[N]) == (&str[0]) + N

    oh! bedeutet also, das auch wenn ich c_str als parameter übergebe, nicht sicher sein kann, das alle elemente hintereinander liegen?
    hmm... dann sollte ich wohl lieber doch ein char* array zwischenschalten`?

    nene, c_str() garantiert schon dass alles hintereinander liegt. als input kannst du nen std::(w)string jederzeit verwenden.

    bloss als output nicht, da c_str() ja einen const-zeiger zurückgibt. und wegcasten solltest du das const wirklich nicht.



  • Ok, danke! 👍



  • legal/illegal schei... schrieb:

    Ich möchte nämlich für eine UTF-8 -> UTF-16LE Konversion die Windowsfunktion MultiByteToWideChar benutzen, und meine Strings auf diese Weise als Parameter übergeben.

    Nimm liber gleich nen besseren String als das std::string zeugs



  • *lieber



  • Ok, dieser Beitrag beantwortet nicht die Frage der Legalität. Aber wozu stellt man die Frage überhaupt?!!!

    int main() 
    { 
    	string str (10,'a');
    	cout << str << endl;
    	return 0;
    }
    

    Es gibt auch noch std::fill aus <algorithm> .



  • viel besserer schrieb:

    Ok, dieser Beitrag beantwortet nicht die Frage der Legalität. Aber wozu stellt man die Frage überhaupt?!!!

    einfach mal alles lesen was er schreibt



  • beantwortet zwar auch nicht die frage nach der legalität, aber die ist ja auch schon geklärt
    aber ich glaube nicht (mehr ^^), dass man eine wirklich standard-konforme und effektive string-klasse ohne vector hinbekommt - also wird (obwohl es implementation defined ist) imho der code aus dem TO-post funktionieren.

    aber ich hab noch immer nicht verstanden, warum der TO nicht mit dem op[] bzw mit iteratoren arbeiten möchte...

    bb



  • huestbar schrieb:

    Man darf sogar ganz offiziell Änderungen über den operator [] machen. Bloss fehlt bei std::string eben die Garantie, wie es sie bei std::vector gibt, dass &str[0] der Anfang eines zusammenhängenden Speicherbereichs ist, wo alle Elemente hintereinander abgelegt sind.
    Etwas formaler: die Garantie dass (&str[N]) == (&str[0]) + N

    Die Garantie steht in den letzten Working Drafts drin. Und zwar fast genauso:

    WD §21.3.1/3 basic_string general requirements schrieb:

    The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s , the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size() .

    Und operator[n] ist definiert über *(begin() + n) . Also darf man das inzwischen. Ich weiß aber nicht, wann diese Garantie aufgenommen wurde, sie kam auf jeden Fall später als die für std::vector<T> . Es ist auch noch nicht lange her, dass ich zufällig über diese Stelle im Working Draft gestolpert bin.

    Deswegen habe ich auch einmal vor kurzem an anderer Stelle geschrieben, dass std::basic_string<T> nur noch ein auf Zeichenketten optimierter, monolithischer Vektor ist. Der Umweg über std::vector<T> ist also nicht mehr notwendig.

    Also: Der Code müsste valides C++0x sein, ist aber undefined behaviour in C++98 und C++03.



  • Danke für den Hinweis!

    Der Umweg über std::vector<T> ist also nicht mehr notwendig.

    Also: Der Code müsste valides C++0x sein, ist aber undefined behaviour in C++98 und C++03.

    Naja. Genaugenommen ist der C++03 Standard immer noch der aktuell gültige.
    Werd's mir aber für die Zukunft merken 🙂



  • Old McDonald schrieb:

    Und operator[n] ist definiert über *(begin() + n) . Also darf man das inzwischen.

    Das allein (also ohne das, was Du vorher geschrieben hast) garantiert allerdings nicht, dass die Elemente hintereinander im Speicher liegen. basic_string<>::begin() muss ja keinen Zeiger zurückgeben.

    BTW: Die &*(s.begin() + n) == &*s.begin() + n -Garantie gibt es scheinbar nicht in C++03 -- sie ist zumindest nicht in N1804 von 2005 drin, wenn ich nichts übersehen habe.

    Gruß,
    SP


Anmelden zum Antworten