#c
#c
Вопрос:
Я создал некоторую цепочечную структуру, где один объект имеет указатели на следующий и предыдущий объект цепочки. Приведенный ниже код перебирает всю цепочку, ищет значение, указанное в аргументах, и удаляет соответствующий элемент (если он существует).
void List::removeElementByValue(int value)
{
ListMember* nextElem = this->firstValue;
while (nextElem) {
if (nextElem == NULL || nextElem == nullptr) {
break;
}
if (nextElem->value == value) {
if (nextElem->prevValue)
(nextElem->prevValue)->nextValue = nextElem->nextValue;
if (nextElem->nextValue)
(nextElem->nextValue)->prevValue = nextElem->prevValue;
delete nextElem;
this->count--;
return;
}
nextElem = nextElem->prevValue;
}
}
Проблема в том, что я получаю эту ошибку, когда пытаюсь удалить несуществующее значение из цепочки.
Возникло исключение: нарушение доступа на чтение. Следующий элемент был 0XCDCDCDCDCD.
Функция в этом случае ничего не должна делать. Это происходит в этой строке:
if (nextElem->value == value) {
Как вы видите, я использовал несколько способов проверить правильность nextElem, но я все еще получаю эту ошибку. Есть ли способы, которыми я могу это предотвратить?
Комментарии:
1. Скорее всего, у вас неинициализированный указатель — чтение его значения является неопределенным поведением , вы не можете «защититься» от этого. Вам нужно убедиться, что оно инициализировано
2. Лучшая защита в C — это использование современной альтернативы необработанным указателям, таким как
unique_ptr
,shared_ptr
илиweak_ptr
. Сам язык не дает вам никакой защиты от недопустимого доступа к памяти, но хороший стиль и соглашение могут.3. Я создал некоторую цепочечную структуру — другими словами, двусвязный список, а другими словами, снова std::list<int> . Рассмотрите возможность использования этого вместо.
4. Незначительно связанные: в Википедии есть хороший список распространенных магических чисел для отладки и их значений. Когда вы получаете число, подобное 0XCDCDCDCDCD, проверьте список, пытается ли программа вам что-то сказать.
Ответ №1:
if (nextElem == NULL || nextElem == nullptr)
Это всегда будет false, когда while (nextElem)
имеет значение true.
nextElem = nextElem->prevValue;
Это нужно использовать nextValue
вместо prevValue
.
Но, самое главное, вы не обновляете, this->firstValue
если value
находится в первом элементе списка, поэтому вы в конечном итоге удаляете firstValue
и оставляете его указывающим на недопустимую память.
Попробуйте это вместо:
void List::removeElementByValue(int value)
{
ListMember* elem = this->firstValue;
while (elem) {
if (elem->value == value) {
if (elem->prevValue)
elem->prevValue->nextValue = elem->nextValue;
if (elem->nextValue)
elem->nextValue->prevValue = elem->prevValue;
// ADD THIS!!!
if (elem == this->firstValue)
this->firstValue = elem->nextValue;
delete elem;
this->count--;
return;
}
elem = elem->nextValue; // NOT prevValue!
}
}
Лучшее решение — в первую очередь не внедрять связанный список вручную. Вместо этого используйте стандартный std::list
контейнер, пусть он сделает все самое сложное за вас.
#include <list>
class List
{
private:
std::list<int> values;
...
};
...
#include <algorithm>
void List::removeElementByValue(int value)
{
auto iter = std::find(values.begin(), values.end(), value);
if (iter != values.end())
values.erase(iter);
}