C++ 0xC0000005 : Zugriffsverletzung beim Lesen an Position xxx

KingJoshii1000

Lieutenant
Registriert
Apr. 2010
Beiträge
879
Hallo,
ich beschäftige mich zurzeit mit dem Schreiben u. Lesen von Binarydaten.
Nun habe ich ein kleines Programm geschrieben (dient keinem ernsthaften Zweck, nur als Übung) welches eine von mir erstellte Klasse in Binaryform in eine Datei schreibt und diese wieder auslesen soll. Das schreiben klappt super, jedoch bekomme ich die oben genannte Exception von Visual Studio nachdem ich auf den break treffe.
Hier der Sourcecode
Code:
//Codeausschnitt (#include etc. ist vorhanden)
class test{
public:
	test()
		:wert(0), name("Null"){};
	test(string Name, int Wert)
		:wert(Wert),name(Name){};
	string get_name(){
		return name;
	}
	int get_value(){
		return wert;
	}
private:
	string name;
	int wert;
};
int main(){
	char input = ' ';
	test temp("Test_Objekt", 999);
	while (true){
		cin >> input;
		switch (input){
		case's':{
					ofstream ofstr("log.binary", ios_base::out | ios_base::binary);
					ofstr.write(as_bytes(temp), sizeof(test));
					ofstr.close();
					break;
		}
		case'a':{
					test temp_read;
					ifstream ifstr("log.binary", ios_base::in | ios_base::binary);
					ifstr.read(as_bytes(temp_read), sizeof(test));
					cout << temp_read.get_name() << endl;
					break;    <----------- Beim debuggen kommt hier die Exception.
		}
		default:{
						 cout << "Befehl unbekannt." << endl;
                                                 break;
		}
		}
	}
	keep_window_open();
}
Die Exception lautet wortgenau: Ausnahmefehler bei 0x5431DF58 (msvcp120d.dll) in Uebung_01.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00789584.
Wo genau liegt der Fehler?
Habe den Fehler bisher nur bei Binary Daten erhalten, jedoch fehlt mir noch das nötige KnowHow mit den Binary Sachen welche ich mir zurzeit aneigne.

Mit freundlichen Grüßen :)
 
Hi,

das Problem wird von deinem String-Member kommen. Den kannst Du nicht einfach binary rausschreiben und einfach wieder so einlesen.
Der String ist nur ein Container, d.h. der eigentliche Inhalt kann ganz woanders im Speicher liegen.
Zum Nachdenken: sizeof(test) ist unabhängig von der Länge des Strings. Wie solltest Du damit einen String mit z.B. 1000 Zeichen Länge korrekt rausschreiben können?

Du schreibst jetzt die Verwaltungsstrukuren des Strings des Objekts 'temp' binär raus und lädst das wieder in das Objekt 'temp_read'.
Nach deinem break geht 'temp_read' out of scope und wird zerstört. Dabei versucht es, seinen eigenen String-Member 'name' auch zu zerstören. Da stehen aber die Verwaltungsstrukturen von temp.name drin. Das kann ja nur schiefgehen.


Gruß,
Jürgen
 
Hallo,
danke für die kleine Aufklärung:). Also liegt das Problem in der Class Test, genauer am String Member?
Wie genau kann ich den mein Vorhaben umsetzen?
Habe bisher in dieser Richtung mit Strings keine Erfahrungen machen können. Habe bisher das ganze nur mit Integer Werten getestet und das funktionierte super:).

Mit freundlichen Grüßen
 
Du möchtest den Inhalt des string-Objektes speichern, nicht das string-objekt selbst. An den Inhalt eines std::string-Objektes kommst du über dessen c_str()-Methode.
 
Hallo,
ich würde gerne die ganze Klasse (soweit das überhaupt möglich ist) in eine Binary Datei speichern, sodass ich sie später nur auslesen muss und die vollständige Klasse wieder habe. Wie setze ich das am besten um? Ist es eine Möglichkeit, der Klasse den String Member zu entfernen und diesen dann seperat zu speichern? Oder kann man wie ich es oben schon versucht habe, die ganze Klasse speichern, jedoch mit "vorbereitetem" String? Wie gesagt, ich bin dort leider nicht so bewandert.

Mit freundlichen Grüßen
 
Ich würde einfach ein Struct mit allen benötigten Daten erstellen (keine Zeiger verwenden, also z.B. kein char* Text; in das Struct packen, sondern stattdessen z.B. char Text[64];), den Inhalt der Klasse in das Struct kopieren und das Struct in eine Datei schreiben. Beim Auslesen dann genau andersherum.

Um Strings mit unterschiedlicher Länge zu speichern, einfach einen Header mit der Größenangabe vor den String in die Datei packen. Geht auch mit anderen Daten, die keine feste Größe haben, du musst halt nur Typ (z.B. 0 für String, 1 für ein Keyframe-Array, 2 für ...) und Größe des Objektes speichern. Also einfach ein Struct Header mit unsigned int Size; unsigned short Type; in die Datei schreiben und wieder auslesen.
 
Zuletzt bearbeitet:
Das ist generell keine gute Idee, einfach binär den Speicher rauszuschreiben und wieder einzulesen.
Ein Problem hast Du ja schon, sobald du irgendwas mit Zeigern drin hast (hast Du im Prinzip im String).

Sobald Du mal etwas komplizierte Klasskonstrukte mit virtuellen Funktionen hast, wird's wieder heikel
Stichwort: vtable
Die beinhaltet dann nämlich wieder Zeiger (und zwar auf Methoden). Und es wird dir nicht garantiert, dass die zwischen zwei Programmabläufen identisch sind.

Wenn du unbedingt sowas brauchst, kannst Du entweder über die Stream-Operatoren oder zum Einstieg über Methoden arbeiten (sowas wie toString oder fromString)
 
Hallo,
mir ging es primär darum, erstmal Erfahrung mit Binärdaten zu sammeln. Also ist es nicht oder nur sehr schwer Möglich eine Klasse (welche Strings enthält) in Binärform zu speichern und einzulesen?
Würde gerne Klassen ohne großen Aufwand in Daten speichern und einlesen. Habe das bisher über ofstreams gemacht, die die Werte z.B so geschrieben haben "1:2:Hallo" und ifstreams, die mit dem Format umgehen konnten. Daher wollte ich schauen, ob es da noch andere Wege z.B Binardaten zu realisieren ist?

Wünsche ein schönes Wochenende.
Mit freundlichen Grüßen


Mit freundlichen Grüßen
 
Das Problem, vor dem du stehst nennt man Serialisierung oder Marshaling. Dabei willst du Informationen in deiner Klasse in eine sinnvolle Form bringen, um sie anschließend zu speichern.
Das wirklich generisch zu machen ist schwer, da ja jede Klasse unterschiedliche Membervariablen und so weiter hat. Deshalb musst du das eigentlich für jede Klasse wieder neu implementieren. Du kannst natürlich das speichern und so weiter in eine Funktion auslagern und nur pro Klasse manuell angeben, welche Klassenvariablen gespeichert werden sollen udn welche nicht.

Ich würde den operator<< und operator>> der Klasse überladen. Dann kannst du im Grunde sowas machen:
Code:
MeineKlasse objekt;
// objekt initialisieren
std::ifstream file;
// Datei öffnen etc.

// Dann speichern. Der operator<< kümmert sich um das Marshaling
file << objekt;

Und zum Lesen dann z.b. einfach
Code:
file >> objekt
Dabei kümmert sich dann der operator>> wieder ums Auslesen. Der Vorteil ist halt, dass dann alles schön in der Klasse gekapselt ist.
 
Ich weiß nicht, ob die << und >> Operatoren eine gute Wahl für dieses Vorhaben sind. Was ist, wenn jetzt jemand den << Operator nicht auf ein ofstream-Objekt sondern z.B. auf das cout-Objekt anwendet? Dann kommt nur Grütze auf den Bildschirm, da deine Überladung ja im binär-Format und nicht in einer menschlich-lesbaren Form speichert.

Ich würde für diesen Fall lieber 2 sprechende Methodennamen wählen (z.B. saveToFile, loadFromFile).
 
Zuletzt bearbeitet:
Da hast du natürlich Recht, daran habe ich gar nicht gedacht. Kann man nicht dynamisch abfragen, ob das ein fstream oder cout-Objekt ist?
 
Hmm, wenn ich jetzt drüber nachdenke ... vielleicht geht's doch. Man müßte doch operator << statt für std::ostream und alle davon abgeleiteten Klassen auch speziell für std::ofstream überladen können. Oder?

Bin mir im Moment nicht sicher, da ich bisher immer nur für std::ostream überladen habe und es mir dann Wurscht war, ob das Ding auf ein ofstream, ein ostringstream oder irgend was anderes von std::ostream abgeleitetes angewendet wurde.

P.S. Drecks-Smilies. Kann man das irgend wie abstellen?
 
Klar gehts. Einfach den Operator für ostream und ofstream überladen.
Code:
#include <iostream>
#include <fstream>
#include <sstream>

class foo
{
};

std::ofstream& operator<<(std::ofstream& stream, const foo&)
{
	std::cout << "fstream" << std::endl;
	return stream;
}

std::ostream& operator<<(std::ostream& stream, const foo&)
{
	std::cout << "ostream" << std::endl;
	return stream;
}

int main(int, char **)
{
	foo bar;
	std::cout << bar;

	std::ofstream file;
	file << bar;

	std::stringstream stream;
	stream << bar;

	return 0;
}
gibt wie zu erwarten aus:
ostream
fstream
ostream
 
Zurück
Oben