Странная ошибка сегмента: указатель на функцию возвращает значение - PullRequest
2 голосов
/ 15 июля 2011

Изучая C #, я находил интересным переопределять такие вещи, как List или LinkedList, просто чтобы понять, как он работает, и возможные проблемы, которые могут возникнуть при его реализации.

Во время изучения C ++, поскольку у меня есть некоторый опыт вВ C # я решил бросить вызов самому себе и попытаться реализовать более продвинутый код, чем то, о чем говорится в конце главы.Итак, я попытался реализовать не универсальный список в C ++, чтобы попробовать его, но в итоге получил очень странную ошибку сегмента.

Небольшой отказ от ответственности в коде, в то время как пытаясь исправить это, я закончилрефакторинг и удаление чего-либо (хотя ни одно из них не изменило ошибку), чтобы одна или две функции не имели смысла, но после нескольких часов попыток понять проблему, я не хочу ничего удалять или изменять и случайно решаю проблему.Во всяком случае, вот код.

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];
}

Я обычно не публикую, но Google широко.В итоге я установил и запустил valgrind, что приводило к ошибкам чтения и записи в точке интереса 1, когда использовалась точка интереса 2.Однако, когда точка интереса два была закомментирована и использовались закомментированные строки, проблем не возникало.

Проблема возникает только после 128 итераций, то есть она корректно удваивается до 64 и 128, а также удаляет массивы.правильно.

Кроме того, как примечание, код отлично работал на Windows, скомпилированной с g ++.

Я попытался воспроизвести ошибку, используя отдельный класс, но он работал отлично.

Опять же, я знаю, что должен использовать стандартные контейнеры (и я буду), но мне нравится все понимать, и тот факт, что я не могу понять это, чрезвычайно раздражает.В сочетании с тем фактом, что я не могу даже воспроизвести его, и мне пришлось скопировать этот неполный и плохо спроектированный код, это только ухудшает ситуацию.Заранее спасибо за помощь!

Незначительное редактирование, если действительно трудно читать, я добавлю комментарии и попытаюсь очистить код, не нарушая (ну, скорее исправляя).ОС, на которых он тестировался, была Windows 7 с mingw (которая работала) и Debian с g ++ (которая работала только с закомментированными строками).

1 Ответ

4 голосов
/ 15 июля 2011

Проблема с оператором

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

заключается в том, что между извлечением значения поля ListData и вызовом функции-члена, на которую указывает NextNumber, нет последовательности.Таким образом, компилятор совершенно счастлив сделать эту загрузку перед вызовом функции, а затем выполнить ее индексацию после вызова.Однако этот вызов может привести к перераспределению ListData, поэтому указатель, полученный от него до вызова, теперь болтается (он указывает на только что удаленный массив), и происходят плохие вещи.вызов функции должен произойти до выборки ListData, так что выборка всегда получит правильное значение после перераспределения изменения размера.

...