Warum ist mein C ++ Code, um einen Segmentierungsfehler verursacht auch nach der Lese mit (...) Funktion?

StackOverflow https://stackoverflow.com/questions/677444

Frage

Meine Anwendung auf einer Codezeile wird die Aussetzung, die nichts falsch mit ihm zu haben scheint, aber mein IDE erscheint mit dem Fehler auf diese Linie wurde zur Aussetzung:

  

gdb / mi (24/03/09 13.36) (. Exited Signal 'SIGSEGV' empfangen. Beschreibung: Segmentation fault).

Die Codezeile ruft einfach eine Methode, die keinen Code in ihm hat. Ist das nicht ein Segmentierungsfehler, wenn Sie eine Null-Referenz haben? Wenn ja, wie kann eine leere Methode eine Null-Referenz hat?

Dieses Stück Code, scheint das Problem zu verursachen:

#include <sys/socket.h>

#define BUFFER_SIZE 256

char *buffer;

buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();
int writeResult = write(socketFD, buffer, BUFFER_SIZE);

bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

Wenn die Linie, die die read(...) Methode wird kommentiert heraus, das Problem verschwindet.

Update:

Ich habe die Frage geändert zu dem eigentlichen Problem zu zeigen, und ich habe alle irrelevanten Code entfernt - und ich auch beantwortete meine eigene Frage so dass die Menschen dies konkret wissen, zu lesen, was das Problem ist, bitte lesen Sie meine Antwort, bevor er sagt: „Du bist ein Idiot!“.

War es hilfreich?

Lösung

Der Code ist falsch: Pufferpunkte auf einige zufällige Stück Erinnerung. Ich bin mir nicht sicher, warum die Linie mit bzero nicht versagen.

Der richtige Code lautet:

   char buffer[BUFFER_SIZE];

   bzero(buffer, BUFFER_SIZE);
   int readResult = read(socketFD, buffer, BUFFER_SIZE);

oder Sie können calloc verwenden (1, BUFFER_SIZE) einige Speicher zugewiesen zu bekommen (und auf Null gesetzt).

Andere Tipps

Zuerst wird ein Verfahren durch einen Null-Zeiger oder Referenz Aufruf ist streng genommen nicht definiertes Verhalten. Aber es kann gelingen, wenn der Anruf virtuell ist.

Beim virtuellen Methoden praktisch (über einen Zeiger / Referenz, nicht von der abgeleiteten Klasse mit Klasse :: Methode () Art und Weise der Invokation) nicht immer, wenn der Verweis / Zeiger ist null, weil virtuelle Anrufe Zugriff erfordern die VTable Vtable und den Zugriff auf durch einen null-Zeiger / Referenz ist unmöglich. So können Sie nicht eine leere virtuelle Methode durch einen Verweis / Zeiger nennen.

Um dies zu verstehen, was Sie wissen müssen, um mehr darüber, wie Code organisiert wird. Für jede Nicht-Inline-Methode gibt es einen Abschnitt von Code-Segment des Maschinencode Durchführung des Verfahrens enthält.

Wenn ein Anruf durchgeführt wird, nicht praktisch (entweder von einer abgeleiteten Klasse oder eine nicht-virtuelle Methode durch eine Referenz / pointer) der Compiler weiß genau, welche Methode aufgerufen werden (kein Polymorphismus). So fügt es nur einen Anruf auf einen exakten Teil des Codes und übergibt das Zeiger als ersten Parameter gibt. Im Fall eines durch Null-Zeiger-Aufruf das wird auch null sein, aber Sie nicht, wenn Ihre Methode leer ist.

Wenn ein Anruf praktisch durchgeführt wird (durch einen Verweis / Zeiger) der Compiler nicht weiß, was genau das Verfahren zu nennen, sie weiß nur, dass es eine Tabelle der virtuellen Methoden und die Adresse der Tabelle in dem Objekt gespeichert wird. Um zu finden, welche Methode es ist zum ersten dereferenzieren den Zeiger / Verweis notwendig zu nennen, auf den Tisch bekommen, um die Adresse der Methode von ihm bekommen und erst dann die Methode aufrufen. Lesen der Tabelle wird in der Laufzeit, nicht während der Kompilierung durchgeführt. Wenn der Zeiger / Verweis null Sie Segmentierungsfehler an dieser Stelle erhalten.

Dies erklärt auch, warum virtuelle Anrufe nicht inlined werden kann. Der Compiler hat einfach keine Ahnung, welchen Code inline, wenn es an der Quelle während der Kompilierung suchen.

Ohne Code, das Beste, was ich tun kann, ist eine wilde Vermutung. Aber hier geht:

Ihr "long-running code" auf einen ungültigen Zeiger schreibt. (Entweder ein völlig zufällig Zeiger oder geht über den Anfang / Start eines Puffers oder Array). Dies geschieht, um die virtuelle Funktionstabelle für Ihr Objekt clobbering werden. - entweder es den Zeiger auf das Objekt zu überschreiben, oder das vptr Mitglied des Objekts, oder es ist für diese Klasse der tatsächliche globale virtuelle Funktionstabelle zu überschreiben

Einige Dinge zu versuchen:

  • Setzen Sie ein Sentinel-Mitglied in Ihrer Klasse. Z.B. ein int, die mit einem bekannten Muster initialisierte (0xDEADBEEF oder 0xCAFEBABE üblich ist) in Ihrem Konstruktor, und nie geändert. Bevor Sie die virtuelle Funktion Anruf zu tätigen, überprüfen (behaupten ()), dass es immer noch den richtigen Wert hat.
  • Versuchen Sie, einen Speicher-Debugger. Unter Linux Optionen gehören Electric Fence (efence) oder Valgrind.
  • Führen Sie Ihr Programm unter einem Debugger (GDB ist in Ordnung) und schnüffelt, um zu sehen, was los ist - entweder post-mortem nach dem segfault geschieht oder durch einen Haltepunkt kurz vor dem Ort Einstellung, es wird segfault
  • .

Ich kann nicht aus irgendeinem Grund denken, warum ein leerer Methode auf eigene würde ein solches Problem verursachen. Ohne einen anderen Zusammenhang, mein erster obwohl wäre, dass ein Problem an anderer Stelle Ihr Gedächtnis korrumpiert und es passiert einfach so zu sich hier auf diese Weise zu manifestieren.

Wir hatten diese Art eines Problems vor, und ich schrieb darüber in dieser Antwort hier . Das gleiche Frage hat eine Menge anderer guter Rat darin zu, die Sie helfen könnten.

  

Ist das nicht ein Segmentierungsfehler, wenn Sie eine Null-Referenz haben?

Möglich, aber nicht unbedingt. Was ein segfault verursacht, ist etwas plattformspezifisch, aber es bedeutet im Grunde, dass Ihr Programmspeicher zugreift, es sollte nicht sein. Sie könnten die wikipedia Artikel bekommen eine bessere Vorstellung davon lesen wollen, was es ist.

Eine Sache, die Sie überprüfen können, wird die leere Methode einen Rückgabetyp haben? Ich könnte dies falsch sein, aber wenn es ein Objekt zurückgibt, konnte ich sehen, wie eine Kopie Konstruktor kann auf Müll aufgerufen, wenn das Verfahren nicht tatsächlich ein Objekt zurück. Dies könnte alle möglichen wackelig Verhalten führen.

Haben Sie das gleiche Ergebnis, wenn Sie dessen Rückgabetyp ändern ungültig zu erklären oder Sie geben einen Wert zurück?

Das Problem ist, weil die buffer Variable nicht zugewiesen Speicher verwendet, die Speicherfehler verursacht, wenn die read(...) Funktionsdaten in buffer setzen.

Normalerweise bzero würde die Segmentierungsfehler tatsächlich dazu führen, sondern weil eine Zeichenfolge an den Speicherplatz zugewiesen wird, wurde die Lesefunktion hinter dem zugewiesenen Speicher schreiben erlaubt (wodurch das Leck).

/* this causes *some* memory to be allocated, 
 * tricking bzero(...) to not SIGSEGV */
buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();

int writeResult = write(socketFD, buffer, BUFFER_SIZE);

Diese Änderung löst das Speicherleck:

#define BUFFER_SIZE 256

// Use memory on the stack, for auto allocation and release.
char buffer[BUFFER_SIZE];

// Don't write to the buffer, just pass in the chars on their own.
string writeString = GetSomePointer()->SomeStackMemoryString;
int writeResult = write(socketFD, writeString.c_str(), writeString.length());

// It's now safe to use the buffer, as stack memory is used.
bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

Rufen Sie die virtuelle Methode aus dem Konstruktor einer Basisklasse? Das könnte das Problem sein: Wenn Sie eine rein virtuelle Methode aus der Klasse Base in Base Konstruktor anrufen, und es ist eigentlich nur in der Klasse Derived definiert ist, können Sie einen VTable-Datensatz Zugriff am Ende, die noch nicht gesetzt worden, weil Derived Konstruktor wurde an diesem Punkt.

nicht ausgeführt
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top