Komisches Verhalten von std::string?
-
Hallo, wenn ich mir den string moduleStr so ausgeben lasse:
cout<<moduleStr<<"\n";
dann wird nichts ausgegeben.
So wiederum kann ich z.b. einzelne Zeichen ausgeben:
cout<<moduleStr[0]<<"\n";
Und so kann ich mir alles aus dem string anzeigen lassen:
cout<< moduleStr.c_str() << "\n";
Warum sehe ich nur alles wenn ich meinen std::string in einen C-String umwandele?
#include <iostream> using namespace std; int main() { string moduleStr; _asm { mov eax,dword ptr fs:[0x18] // teb address mov eax,dword ptr [eax+0x30] // peb address mov eax,dword ptr [eax+0x0c] // ldr mov eax,dword ptr [eax+0x1c] // InInitializationOrderModuleList mov eax,[eax] // Save Pointer of InInitializationOrderModuleList in eax add eax,0x8 // eax points to kernel32.dll address sub eax,0x50 // eax points to "kernel32.dll" string mov esi,eax mov edi,dword ptr [moduleStr] lab: mov al,[esi] mov [edi],al add esi,2 // +2 because of unicode example: k.e.r.n.e.l.3.2.d.l.l.. the . represents a nullbyte inc edi cmp al,0x0 jne lab term: } cout<< moduleStr.c_str() << "\n"; system("PAUSE"); return 0; }
-
Entweder musst du erklären, was der asm-Code genau macht oder ich verschiebe den Thread ins Assemblerforum.
Spekulation: Du hast keine Ahnung wie ein string funktioniert und zerschießt dessen interne Strukturen. Diese Spekulation ist fundiert, weil du gar nicht wissen kannst, wie der string intern funktioniert, weil die Implementierung nicht vorgegeben ist.
Weitergehende Spekulation: Der String wird vermutlich als ersten Member einen Zeiger auf seine Daten haben, an diesem fummelst du rum. Die anderen Member, zum Beispiel einen Zeiger auf das Ende der Daten, lässt du unverändert. Die "richtige" Ausgabemethode (also ohne .c_str()) gibt die Daten von Anfang bis Ende aus, jedoch wird das Ende vermutlich noch 0 oder uninitialisiert sein, daher wird nichts ausgegeben. c_str() und operator[] hingegen verlassen sich darauf, dass der Zeiger auf den Anfang der Daten richtig ist (zurecht, denn man soll nicht an den internen Strukturen rumspielen) und arbeiten einfach auf diesem. Daher funktioniert die Ausgabe zufällig doch, wenn du diese benutzt. Dies erklärt alles was du beobachtest. Diese Spekulation beruht darauf, dass die Implementierung von String mittels Anfang-, Ende- und reserviertes Endezeiger recht üblich ist.
-
So mal eben eingeloggt.^^
Ok, kurze Erklärung zum ASM Code.
In Windows gibt es verschiedene Strukturen. TEB,PEB, LIST_ENTRY bla bla
Aus einer dieser Strukturen lese ich bestimmte Werte aus.
Diese sind als Unicode gespeichert, und die will ich auslesen.Mein String steht im Speicher an der Adresse: 0x00883548
Der String den ich auslesen will heisst: kernel32.dllDas k vom String kernel32.dll schreib ich an Adresse 00883548.
Das e schreib ich an Adresse 00883549 und so weiter.Ich hab den String zum Test gerade mal initialisiert ( string moduleStr="0000000000000"; ) und dann funktioniert auch die normale Ausgabe ohne C-String.
Tja, jetzt müsste ich wohl wissen wie der std::string intern genau funktioniert.^^ (Das ist bestimmt wieder viel Arbeit
)
Oder ich nehm einfach ein Statisches mit Null Initialisiertes Array.
:xmas1: :xmas2:
-
Wer hat dir erlaubt, an der Repräsentierung eines std::string-Objekts rumzuspielen? Keiner.
jetzt müsste ich wohl wissen wie der std::string intern genau funktioniert
Das hast du nicht wissen zu müssen.
-
krümelkacker schrieb:
Wer hat dir erlaubt, an der Repräsentierung eines std::string-Objekts rumzuspielen? Keiner.
Das braucht mir keiner zu erlauben ich darf das. Ich bin der Boss !
krümelkacker schrieb:
Das hast du nicht wissen zu müssen.
Ok, aber interessant wäre es dennoch.
-
Beschreibe im asm-Code einen const char* und benutze den, um damit den String zu initialisieren.
Es ist absolut verboten eine Klasse ohne Nutzung der öffentlichen Schnittstellen zu modifizieren.Stell dir vor der String wäre refcounted, dann wäre die Hölle los.
Ein String kann auf sehr viele unterschiedliche Arten implementiert sein. Wenn du es konkret für deine Implementierung wissen willst, schau in den Header. Erwarte aber nicht dass es mit einer anderen Implementierung der Standardbibliothek oder mit einer anderen Version deiner auch klappt.
-
Bassmaster schrieb:
krümelkacker schrieb:
Das hast du nicht wissen zu müssen.
Ok, aber interessant wäre es dennoch.
Das ist ja gerade der Witz an so einer Klasse wie std::string, dass man nicht wissen muss, wie die implementiert ist, sondern nur ihre Schnittstelle zu kennen braucht, um std::string benutzen zu können.
Ich bin mir sicher, dass du auch herausfinden kannst, wie std::string funktioniert, wenn du dir den entsprechenden Header (und seine Abhängigkeiten) anschaust. std::string ist nämlich im Sinne des C++ Sprachkerns ein Benutzer-definierter Typ, der dir typischerweise in Quellform zur Verfü+gung steht.
Der andere Witz an std::string ist, dass der C++ Standard nur die Schnittstelle spezifiziert und den Compiler-Herstellern bzw Bibliotheksautoren gewisse Freiheiten lässt. Beispielsweise funktioniert std::string aus libstdc++ vom GCC ganz anders als Microsoft's std::string Implementierung. Ersterer verwendet Referenzzählung und hat std::string so implementiert, dass sizeof(char*)==sizeof(std::string) gilt. Microsoft hat auf Referenzzählung verzichtet und seine Klasse für kurze Strings optimiert (small string optimization), wobei kurze Strings direkt im Objekt gespeichert werden und längere indirekt, also im Freispeicher. Deswegen ist so ein Ding, also sizeof(std::string) bei Microsoft auch viel größer ... mindestens 16 Bytes oder sowas, die Zahl habe ich nicht genau im Kopf.
Der Punkt ist nur, dass du nicht irgendwelche Implementierungsdetails voraussetzen solltest, weil du damit unportablen und zerbrechlichen Code erzeugen würdest. Zerbrechlich deswegen, weil in einer neuen Compiler-Version die std::string-Implementierung geändert werden könnte und dann läuft dein Programm nicht mehr.
-
Ethon schrieb:
Stell dir vor der String wäre refcounted, dann wäre die Hölle los.
Ein String kann auf sehr viele unterschiedliche Arten implementiert sein.Seit C++11 kann ein string nicht mehr refcounted sein.
-
refcounted schrieb:
Seit C++11 kann ein string nicht mehr refcounted sein.
Ich glaube, das ist Quatsch, den du nur nachplapperst.
Kannst du das denn auch belegen?
-
Ethon schrieb:
Beschreibe im asm-Code einen const char*
Höö?
-
krümelkacker schrieb:
refcounted schrieb:
Seit C++11 kann ein string nicht mehr refcounted sein.
Ich glaube, das ist Quatsch, den du nur nachplapperst.
Kannst du das denn auch belegen?21.4.1/6 schrieb:
References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated
by the following uses of that basic_string object:
— as an argument to any standard library function taking a reference to non-const basic_string as an
argument.233
— Calling non-const member functions, except operator[], at, front, back, begin, rbegin, end, and
rend.Damit std::string geshared (refcounted) sein könnte, müsste operator[] die Iteratoren ungültig machen.
-
krümelkacker schrieb:
refcounted schrieb:
Seit C++11 kann ein string nicht mehr refcounted sein.
Ich glaube, das ist Quatsch, den du nur nachplapperst.
Kannst du das denn auch belegen?Doch das ist schon richtig. In C++11 wurde geändert, was Iteratoren und Referenzen auf basic_string-Objekte ungültig macht. Der Sinn dahinter ist bessere Unterstützung für multithreaded-Zugriffe. Dadurch wird indirekt ref-counting ausgeschlossen. Die libstc++-Entwickler sind sich dieser Tatsache bewusst, aber ich weiß nicht, ob dies in der jüngsten Version schon überarbeitet ist. Jedenfalls war die Implementierung eine ganze Weile lang nicht C++11-standardkonform, da noch die alte Implementierung beibehalten wurde.
edit: Viel zu langsam.
-
Danke für das Zitat ... also streng ausgeschlossen wird das jetzt nicht, aber eine ref-counted Implementierung müsste, bevor sie überhaupt Iteratoren oder Referenzen rausgibt, dafür sorgen, dass die Zeichenkette nicht mehr mit anderen string-Objekten geteilt wird. Ob sich das dann noch lohnt ist natürlich fraglich.
Ich habe mal im aktuellen GCC Quellcode reingeschaut. Die letzte Änderung an basic_string.h war vor 5 Wochen. Allerdings scheint sich die Implementierung auf den ersten Blick noch nicht C++11-konform zu sein, da
const_iterator
en einfach so rausgegeben werden, ohne das Sharing vorher aufzuheben.
-
Mein letzter Stand (Sommer 2012, daher ist auch meine aktuelle Compilerversion, die ich jetzt nicht aktualisieren werde) ist, dass eine short-string-Optimierung unter dem Namen vstring (ext/vstring.h) enthalten ist. Genauer gesagt ist das ein ganz cleverer String, bei dem man seine Optimierung selber wählen kann. Aber jedenfalls ist die short-string-Optimierung des vstrings die Testimplementierung die später einmal (oder vielleicht auch jetzt schon in der neuesten Version, ich weiß nicht was aktuell ist) die C++11-konforme Implementierung von std::string werden soll.
-
krümelkacker schrieb:
Danke für das Zitat ... also streng ausgeschlossen wird das jetzt nicht, aber eine ref-counted Implementierung müsste, bevor sie überhaupt Iteratoren oder Referenzen rausgibt, dafür sorgen, dass die Zeichenkette nicht mehr mit anderen string-Objekten geteilt wird. Ob sich das dann noch lohnt ist natürlich fraglich.
Selbst das ist nicht möglich, denn z.B.
begin()
istnoexcept
, also darf keine Reallokation erfolgen.
-
Deswegen finde ich den fbstring aus der Facebook-Library einfach genial: https://github.com/facebook/folly/blob/master/folly/docs/FBString.md
Der kombiniert SSO, "normal" allozierte Strings und COW.
Benutze eigentlich überhaupt keinen std::string mehr bzw ich habe einen Switch in nem Header.out schrieb:
Ethon schrieb:
Beschreibe im asm-Code einen const char*
Höö?
const char* out; __asm mov edi, out std::string s(out);
-
Ok, damit hat sich das ref-counting bei basic_string wirklich erledigt. Ich bin überzeugt.
-
Ethon schrieb:
Deswegen finde ich den fbstring aus der Facebook-Library einfach genial: https://github.com/facebook/folly/blob/master/folly/docs/FBString.md
Der kombiniert SSO, "normal" allozierte Strings und COW.
Nur kann ich die "100%ige std::string-Kompatibilität" nicht glauben, da ja in C++11, wie ich gelernt habe, begin() z.B. noexcept ist. und FBString::begin ggf eine bad_alloc-Ausnahme schmeißt.
-
Die meinen bestimmt 100% kompatibel zu C++98-Strings. Aber dafür in der Praxis schneller als die üblichen Implementierungen.