2 Stimmen

Seltsamer Seg-Fehler mit Funktionszeiger, der einen Wert zurückgibt

Während ich C# lernte, fand ich es lustig, Dinge wie List oder LinkedList neu zu implementieren, nur um zu verstehen, wie es funktioniert und welche potenziellen Probleme man bei der Implementierung haben kann.

Während ich C++ lernte, beschloss ich, da ich einige Erfahrung in C# habe, mich selbst herauszufordern und zu versuchen, einen fortgeschritteneren Code zu implementieren, als es die Aktivitäten am Ende des Kapitels verlangen. So habe ich schließlich versucht, eine nicht-generische Liste in C++ zu implementieren, um sie auszuprobieren, aber am Ende erhielt ich einen sehr seltsamen Fehler.

Ein kleiner Disclaimer auf den Code, beim Versuch, es zu beheben, endete ich es Refactoring und Entfernen von Sachen (nichts davon änderte den Fehler, obwohl) so eine Funktion oder zwei hat keine Verwendung, aber nach ein paar Stunden versuchen, das Problem zu verstehen, ich will nicht zu entfernen oder etwas ändern und versehentlich das Problem beheben. Wie auch immer, hier ist der Code.

class List {
private:
int *ListData;
size_t ListSize;
size_t Pos;
std::stack<size_t> NullList;
size_t InternalNull();
inline size_t PosOnly();

void Check();
size_t (List::*NextNumber)();

public:
List(bool InternalNullHandle);
List(size_t DefaultSize, bool InternalNullHandle);
~List();
void Add(const int SalesRef);
int GetCopy(size_t Pos);
int Get();
};

List::List(bool InternalNullHandle) {
    NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
    ListSize = 32;
    ListData = new int[32];
    Pos = 0;
}

List::List(size_t DefaultSize, bool InternalNullHandle) {
    NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
    ListSize = DefaultSize;
    ListData = new int[DefaultSize];
    Pos = 0;
}

List::~List() {
    delete[] ListData;
}

void List::Check() {
    if (Pos >= ListSize) {
        size_t OldSize = ListSize;
        ListSize*=2;
        int *Buffer = new int[ListSize];
        memcpy(Buffer, ListData, sizeof(int)*OldSize);          
        if (ListData != NULL) {
            delete[] ListData; //POINT OF INTEREST ONE
            ListData = NULL;
        }
        else {
            std::cerr<<"ListData is null."<<std::endl;
        }
        ListData = Buffer;
    }
}

size_t List::InternalNull() {
    if (NullList.size() != 0) {
        size_t ToReturn = NullList.top();
            NullList.pop();
        return ToReturn;
}
return PosOnly();
 }

inline size_t List::PosOnly() {
    size_t Old = Pos;
    ++Pos;
    Check();
    return Old;
}

inline void List::Add(const int SalesRef) {
    //size_t Value = (this->*NextNumber) ();
//ListData[Value] = SalesRef;
//if the above code is utilised instead, everything works fine
    ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO
}

inline int List::GetCopy(size_t Pos) {
    return ListData[Pos];
}

Normalerweise poste ich nicht, aber ich google ausgiebig. Am Ende installierte ich valgrind und führte es aus, was zu Lese- und Schreibfehlern am Point of Interest One führte, wenn Point of Interest Two verwendet wurde. Wenn jedoch Point of Interest Two auskommentiert wurde und die kommentierten Zeilen verwendet wurden, traten keine Probleme auf.

Das Problem tritt erst nach 128 Iterationen auf, d. h. es wird korrekt auf 64 und 128 verdoppelt und die Arrays werden korrekt gelöscht.

Außerdem lief der Code unter Windows, kompiliert mit g++, einwandfrei.

Ich habe versucht, den Fehler mit einer anderen Klasse zu reproduzieren, aber es funktionierte einwandfrei.

Auch hier weiß ich, dass ich die Standardcontainer verwenden sollte (und das werde ich auch), aber ich möchte alles verstehen, und die Tatsache, dass ich das nicht herausfinden kann, ist äußerst ärgerlich. Gepaart mit der Tatsache, dass ich es nicht einmal reproduzieren kann und diesen unvollständigen und schlecht gestalteten Code kopieren musste, macht es nur noch schlimmer. Vielen Dank für die Hilfe im Voraus!

Kleiner Edit, wenn es wirklich schwer zu lesen ist, werde ich Kommentare hinzufügen und versuchen, den Code zu bereinigen, ohne ihn zu zerstören (naja, eher zu reparieren). Die Betriebssysteme, auf denen es getestet wurde, waren Windows 7 mit mingw (was funktionierte) und Debian mit g++ (was nur funktionierte, wenn die kommentierten Zeilen unkommentiert waren).

4voto

Chris Dodd Punkte 109402

Das Problem mit dieser Aussage

ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO

ist, dass es keinen Sequenzpunkt zwischen dem Abrufen des Wertes des Feldes ListData und der Aufruf der Mitgliedsfunktion, auf die durch NextNumber . Der Compiler ist also vollkommen zufrieden, wenn er das Laden vor dem Funktionsaufruf durchführt und dann die Indizierung nach dem Aufruf vornimmt. Dieser Aufruf kann jedoch dazu führen, dass ListData neu zugewiesen wird, so dass der Zeiger, den er vor dem Aufruf erhalten hat, jetzt nicht mehr auffindbar ist (er zeigt auf das gerade gelöschte Array) und schlimme Dinge passieren.

Mit dem auskommentierten Code erzwingen Sie, dass der Funktionsaufruf vor dem Abruf von ListData so dass fetch nach der Größenänderung immer den richtigen Wert erhält.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X