Ошибки SIGABRT и с sysmalloc

267
26 ноября 2016, 18:55

Пишу программу по двоичным деревьям.

Для инициализации дерева я сделал отдельную процедуру, но при её вызове появляется очень странная ошибка.

В коде процедура зовётся initTree. Суть ошибки в том, что после того, как она отрабатывает, не может нормально выводиться текст в консоль, пишет ошибку (тоже в консоль):

Untitled^ malloc.c:2403: sysmalloc: Assertation '(old_top == initial_top (av) && 
old_size == 0) || ((unsigned long (old_size) >= MINSIZE && prev_inus (old_top) && 
((unsigned long) old_end & (pagesize - 1)) == 0)' failed

Также если я запускаю отладчик, qtCreator рассказывает мне про ошибку SIGABRT

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

Я проверял это и с <cstdio>, и с <iostream>, результат был абсолютно одинаков.

Без этой самой процедуры ошибка тоже не появляется.

Вот код программы:

#include <iostream>
#include <cstdio>
using namespace std;
void preparationToCreateTree(int &n, int &posA);
void initTree(int &n, int &posA, int tree[], int numTree[], int &posZ);
int main()
{
    int posA = 1, posZ;
    int n;
    preparationToCreateTree(n, posA);
    int *numTree = new int[posA + n + 3 + 1];
    int *tree    = new int[n + 1];
    initTree(n, posA, tree, numTree, posZ);
    cout << "TESTED" << endl;   //Без строки ниже тут выкидывает ошибку
    return 0;
}

void preparationToCreateTree(int &n, int &posA)
{
    cin >> n;
    while(posA < n)
        posA *= 2;
}
void initTree(int &n, int &posA, int tree[], int numTree[] , int &posZ)
{
    cout << "Ahh.. Touch me, senpai!!";  //Без этой строки появляется ошибка
    tree[0] = -1;
    for(int i = 1; i <= posA + n + 3 + 1 - 1; i++)
        tree[i] = 0;
    for(int i = 1; i <= n; i++)
        cin >> tree[i];
    for(int i = 1; i <= n; i++)
        numTree[posA + i - 1] = i;
    if(n % 2 != 0)
        n++, tree[posA + n - 1] = 0;
    if((n / 2) % 2 != 0)
        n += 2, tree[posA + n - 1] = 0, tree[posA + n - 2] = 0;
    posZ = posA + n - 1;
}

Пишу на archlinux. Пользуюсь компиляторами gxx

Answer 1

Ваша программа имеет неопределенное поведение, так как имеется попытка обращения за пределы выделенной памяти для массивов.

В данном предложении

int *tree    = new int[n + 1];

выделена память для n + 1 объектов типа int

Однако уже в этом цикле

for(int i = 1; i <= posA + n + 3 + 1 - 1; i++)
        tree[i] = 0;

идет обращение к элементам, индекс которых явно больше n в виду условия

i <= posA + n + 3 + 1 - 1

которое проще записать как

i <= posA + n + 3

К тому же внутри функции значение переменной n еще более увеличивается, а память для массива не перераспределяется

if(n % 2 != 0)
    n++, tree[posA + n - 1] = 0;
    ^^^^
if((n / 2) % 2 != 0)
    n += 2, tree[posA + n - 1] = 0, tree[posA + n - 2] = 0;
    ^^^^^^

Обратите внимание на стиль написания кода. Трудно понять, что ваша программа делает.

Старайтесь объявлять функции таким образом, чтобы они выполняли одно действие. Например, в функции preparationToCreateTree ввод значения n следует перенести в другую функцию или в основной код main. В результате функция может быть упрощена и не использовать ссылки

int preparationToCreateTree( int n )
{
    int posA = 1;
    while( posA < n ) posA *= 2;
    return posA;
}

К тому же возникают вопросы, правильно ли вычисляется posA. Должно ли ее конечное значение быть меньше или равно n, или все же это ближайшее сверху значение.

Answer 2

Ничего удивительного, у тебя идут ошибки памяти, потому что ты пишешь за пределы выделенной памяти. У тебя posA равно первой степени двойки БОЛЬШЕЙ n, а под tree выделено (n+1) элементов. Как ты думаешь, куда зайдёт цикл for(int i = 1; i <= posA + n + 3 + 1 - 1; i++) ?

Answer 3
int *tree    = new int[n + 1];
/* ... */
for(int i = 1; i <= posA + n + 3 + 1 - 1; i++)
    tree[i] = 0;

Сколько памяти выделено для tree и сколько данных в него пишется?

И почему одна строчка всё исправляет?

Она ничего не исправляет. Когда запорота память программы может случиться что угодно. Другая строчка при выводе, другое действие, запуск программы из другого каталога, определённое системное время - всё это может замаскировать ошибку, а может и отформатировать винчестер, предварительно удалив все ваши аккаунты из соцсетей.

См. "Неопределённое поведение".

Answer 4

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

Как только вы сталкиваетесь с таким вот поведением - или когда изменения в одном месте вдруг влекут за собой изменение поведения совсем в другом месте, или когда программа под отладчиком работает, а без - нет - 95% (оценка неточная, может, и больше :)), что вы где-то портите память! Пишете за границами, удаляете неудаляемое или удаляете дважды, выделяете малый буфер - словом, перепроверяйте работу с памятью!

READ ALSO
curl русские символы в url

curl русские символы в url

каким образом можно открыть курлом сайт с русскими символами в url?

404
Нерекурсивный поиск в глубину

Нерекурсивный поиск в глубину

Как правильно организовать данный алгоритм?

518