указатели — проверка работоспособности C ++ завершается неудачно: несколько переменных / позиций памяти заменяются мусором, даже если я никогда не получу к ним доступ

Я реализую список пропуска. Не важно, что это такое, но сейчас оно работает для 1000 узлов, но не для 10000. Я получал SegFaults, которые не имели смысла, поэтому я напечатал некоторые переменные. К моему удивлению, многие вещи, которые не должны были меняться, превращались в мусорные ценности. Например, я напечатал inputValue до и после функции insertNode. Иногда он сбрасывается в ноль, когда всегда должен увеличиваться. Давайте посмотрим код (пропустите ввод прочитанного файла, проблема возникает во время цикла):

int main(int argc, char** argv) {
string filename = "";

if( argc == 2 )
filename = argv[1];
else
return 0;

list = new skiplist();

fstream inputFile(filename.c_str(), ios_base::in);

inputFile >> numberofnodes;
inputFile >> list->minimumKey;
inputFile >> list->maximumKey;

printf("%d\n", numberofnodes);
printf("%d\n", list->minimumKey);
printf("%d\n", list->maximumKey);

list->Maxlevel = 1;

list->header = new node();
list->tail = new node();
list->header->key = list->minimumKey;
list->tail->key = list->maximumKey;for ( int i=1; i<=MAXIMUMLEVEL; i++ ) {
list->header->forward[i] = list->tail;
list->tail->forward[i] = NULL;
}

int sanityCheck = 134153;
// insert nodes
int inputKey;
int inputValue = 0;
int * keys = new int[numberofnodes];
while (inputFile >> inputKey)
{
inputValue++;
keys[inputValue] = inputKey;
insertNode(inputKey, inputValue);
if(sanityCheck != 134153)       // dark magic changes this value
keys[9999999999999999999999]++;  // program crashes here
// it would otherwise crash on while
}
printf("\n\nNodes inserted: %d\n\n",inputValue);

Я управлял Вальгриндом. Неправильная запись / чтение памяти произошла после и из-за изменения переменных, по крайней мере, я так считаю. Вот почему я добавил проверку вменяемости. И, как я думал, не было недопустимых операций записи / чтения памяти перед попыткой доступа к ключам [9999999999999999999999]. Но эта строка может только запустить int sanitycheck, который изменился, чего я никогда не делаю.

Наконец, вот код для insertNode. Я не вижу на нем ничего, что могло бы вызвать это:

void insertNode(int newKey, int newValue){
node * update[MAXIMUMLEVEL];
node * auxNode = list->header;
for(int i=list->Maxlevel; i >=1; i--) {
while ( auxNode->forward[i]->key < newKey ) {
auxNode = auxNode->forward[i];
}
update[i] = auxNode;
}
auxNode = auxNode->forward[1];
if ( auxNode->key == newKey ) {
auxNode->value = newValue;
} else {
int randomLevel = 1;
while ( (rand() / double(RAND_MAX)) < LEVELPROBABILITY && randomLevel < MAXIMUMLEVEL ) {
randomLevel++;
}

if ( randomLevel > list->Maxlevel ) {
for ( int i = list->Maxlevel+1; i <= randomLevel; i++ ) {
update[i] = list->header;
}
list->Maxlevel = randomLevel;
}
node * newNode = new node();
newNode->key = newKey;
newNode->value = newValue;
for ( int i=1; i<=MAXIMUMLEVEL; i++ ) {
newNode->forward[i] = NULL;
}

for ( int i=1; i<=list->Maxlevel; i++ ) {
newNode->forward[i] = update[i]->forward[i];
update[i]->forward[i] = newNode;
}
}
}

И структуры:

typedef struct node {
int key;
int value;
node * forward[MAXIMUMLEVEL+1];
}node;

struct skiplist {
int minimumKey;
int maximumKey;
int Maxlevel;
node * header;
node * tail;
};

EDIT:
#define MAXIMUMLEVEL 16
#define LEVELPROBABILITY 0.5

Я даже не использую malloc. Есть операции с указателями, но valgrind должен обнаружить, если я сделал что-то плохое, верно? Если бы у меня не хватало памяти, было бы исключение. Как это возможно, что int, который я создаю и никогда не получаю доступ / запись / изменение, изменяется? Извините за длинный пост, но я понятия не имею, где проблема может быть.

Вывод Valgrind без проверки работоспособности (ключи [999 … 9]): http://pastebin.com/hWH3fri2

Строка 155 — время (inputFile >> inputKey)

0

Решение

Вот вывод дезинфицирующего средства clang (после настроить его правильно):

== 15146 == ОШИБКА: AddressSanitizer: переполнение буфера стека по адресу
0x7ffeb006bb80 на ПК 0x0000004e093c bp 0x7ffeb006ba60 sp 0x7ffeb006ba58

ЗАПИСЬ размера 8 на нити 0x7ffeb006bb80 T0
# 0 0x4e093b в insertNode (int, int) skiplist.cpp: 55: 27
# 1 0x4e3385 в skiplist.cpp: 160: 9
# 2 0x7f40b2fcda3f в __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x20a3f)
# 3 0x419508 в _start (a.out + 0x419508)

Адрес 0x7ffeb006bb80 находится в стеке потока T0 по смещению 160 в кадре
# 0 0x4e022f в insertNode (int, int) skiplist.cpp: 35

Этот фрейм имеет 1 объект (ов):
[32, 160) «Обновление» <== Доступ к памяти по смещению 160 переполняет эту переменную

Строка 55 относится к этому:

void insertNode(int newKey, int newValue){
node * update[MAXIMUMLEVEL];
node * auxNode = list->header;
for(int i=list->Maxlevel; i >=1; i--) {
while ( auxNode->forward[i]->key < newKey ) {
auxNode = auxNode->forward[i];
}
update[i] = auxNode;
}
auxNode = auxNode->forward[1];
if ( auxNode->key == newKey ) {
auxNode->value = newValue;
} else {
int randomLevel = 1;
while ( (rand() / double(RAND_MAX)) < LEVELPROBABILITY && randomLevel < MAXIMUMLEVEL ) {
randomLevel++;
}

if ( randomLevel > list->Maxlevel ) {
for ( int i = list->Maxlevel+1; i <= randomLevel; i++ ) {
update[i] = list->header; // line 55 <===================
}
list->Maxlevel = randomLevel;
}

Петля

while ( (rand() / double(RAND_MAX)) < LEVELPROBABILITY && randomLevel < MAXIMUMLEVEL ) {
randomLevel++;
}

гарантирует, что randomLevel <= MAXIMUMLEVEL, Если randomLevel == MAXIMUMLEVEL, а также MAXIMUMLEVEL > list->Maxlevelтогда цикл в строке 54 превращается в:

for ( int i = list->Maxlevel+1; i <= MAXIMUMLEVEL; i++ ) {
update[i] = list->header; // line 55 <===================
}

Обратите внимание, что update объявлен как node * update[MAXIMUMLEVEL];, Вы получите доступ за границу.


Я не совсем понимаю, почему ваш код не имеет доступа к 0-му элементу массивов. По моему опыту, также гораздо легче работать с полуоткрытыми справа диапазонами формы [0, length_of_array) что приводит к петлям формы

for(int i = 0; i < length_of_array; ++i)

Обратите внимание < вместо <=, Последовательное использование диапазонов полуоткрытого вправо может значительно сократить количество ошибок «один на один».

Быстрое решение — объявить update как node::forward как

node * update[MAXIMUMLEVEL + 1];

Обратите внимание +1,

Лучше исправить это, вероятно, переписать код так, чтобы он использовал полуоткрытые правые диапазоны, где MAXIMUMLEVEL получает его интерпретацию из диапазона [0, MAXIMUMLEVEL) и больше не является максимумом, но является супремумом (и обозначает количество уровней).

0

Другие решения


По вопросам рекламы [email protected]