Ошибка сегментации при инициализации двоичного дерева поиска после второй вставки с использованием итеративной функции вставки

#c #linked-list #segmentation-fault #initialization #binary-search-tree

#c #связанный список #ошибка сегментации #инициализация #binary-search-tree

Вопрос:

Я получаю ошибку сегментации после попытки вставить второй узел в дерево, возникает ошибка сегментации. Ошибка сегментации связана с initialize функцией или insert ? И как мне решить ее, внеся изменения в server.c?

файл client.c:

 int main()
{
    Tree my_tree;
    initialize(amp;my_tree);
    while(1)
    {
        scanf("%d", amp;choice);
        switch (choice)
        {
        case 1:
            scanf("%d", amp;element);
            insert(amp;my_tree, element);
            break;
        case 2:
               exit(0);
       }
    }
    return 0;
}
  

файл server.c

 
typedef struct node
{
    int data;
    struct node *left;
    struct node *right;
} Node;

typedef struct tree
{
    Node *root;
} Tree;

void initialize(Tree *tree)
{
    tree = (Tree*)malloc(sizeof(Tree));
    tree->root = NULL;
}

void insert(Tree *tree, int data)
{
    Node *temp = (Node*)malloc(sizeof(Node));
    temp->left = temp->right = NULL;
    temp->data = data;
    if(tree->root == NULL) 
        tree->root = temp;    
    else
    {
        Node *prev,*curr;
        prev = NULL;
        curr = tree->root;
        while(curr != NULL)
        {
            if(temp->data < curr->data){
                prev = curr;
                curr = curr->left;
            }
            if(temp->data >curr->data){
                prev = curr;
                curr = curr->right;
            }
        }
        if(temp->data < prev->data)
            prev->left = temp;
        if(temp->data > prev->data)
            prev->right = temp;
    }
}
  

Комментарии:

1. Ваша initialize функция будет работать не очень хорошо. Вы пропустили главу в своем учебнике об указателях и передаваемых объектах и что подразумевается, например, под оператором address-of amp; ?

Ответ №1:

 void initialize(Tree *tree)
{
    tree = (Tree*)malloc(sizeof(Tree));
    tree->root = NULL;
}
  

Изменить на

 void initialize(Tree *tree)
{
    if (tree) {
     tree->root = NULL;
    }
}
  

Когда вы выделяете память tree , вы указываете новую ячейку памяти, таким образом, корневой узел вашего оригинала tree не будет инициализирован NULL , а также вы вводите утечки памяти.

Комментарии:

1. Почему это должно быть так? Пожалуйста, попробуйте добавить объяснение, иначе OP может никогда не понять причину.

2. О, хорошо .. я попробовал это. Однако та же проблема сохраняется. Я ввел входные данные как 1 3 и 1 2, после чего он выдал ошибку сегментации

3. одна из возможностей заключается в том, что если вы снова введете то же значение (ex: 1 2 1) , то ваши void insert(Tree *tree, int data) функции будут переброшены, поскольку они непрерывно повторяются и в какой-то момент выходят из строя. можно избежать, добавив if ( temp->data == curr->data ) break; внутри while

Ответ №2:

Вместо инициализации Tree объекта, который вы фактически используете в своей программе ( tree в main), вы выделяете новый Tree объект и инициализируете этот объект. По сути, initialize не делает ничего, кроме утечки памяти.

Чтобы исправить это, замените

 void initialize(Tree *tree)
{
    tree = (Tree*)malloc(sizeof(Tree));
    tree->root = NULL;
}
  

с

 void initialize(Tree *tree)
{
    tree->root = NULL;
}
  

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

Вы могли бы использовать отладчик.

 $ gcc -Wall -Wextra -pedantic -g a.c -o a amp;amp; gdb --args ./a
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
...
(gdb) r
Starting program: /tmp/ikegami/a
1
3
1
2

Program received signal SIGSEGV, Segmentation fault.
0x00005555555547b1 in insert (tree=0x7fffffffdda0, data=2) at a.c:39
39                  if(temp->data >curr->data){
(gdb) print temp
$1 = (Node *) 0x555555756690
(gdb) print curr
$2 = (Node *) 0x0
(gdb) quit
A debugging session is active.

        Inferior 1 [process 1433] will be killed.

Quit anyway? (y or n) y
  

Другой подход можно было бы использовать -fsanitize=address при компиляции программы.

 $ gcc -Wall -Wextra -pedantic -g -fsanitize=address a.c -o a amp;amp; ./a
1
3
1
2
ASAN:DEADLYSIGNAL
=================================================================
==1370==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x556253e76e87 bp 0x7ffe3a28e390 sp 0x7ffe3a28e360 T0)
==1370==The signal is caused by a READ memory access.
==1370==Hint: address points to the zero page.
    #0 0x556253e76e86 in insert /home/ikegami/tmp/a.c:39
    #1 0x556253e771af in main /home/ikegami/tmp/a.c:64
    #2 0x7fc3bdadcb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6 0x21b96)
    #3 0x556253e76b29 in _start (/tmp/ikegami/a 0xb29)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/ikegami/tmp/a.c:39 in insert
==1370==ABORTING
  

Но подход показывает, что temp->data >curr->data это виновник. Используя отладчик, мы увидели, что curr это NULL , so curr->data является проблемой. Теперь вы знаете, что вам нужно выяснить, имеет ли значение why curr значение NULL, когда вы этого не ожидаете.

Комментарии:

1. Я внес это изменение, но, похоже, ошибка не изменилась. Входные данные, которые я даю, равны 1 3, затем 1 2 .. после чего возникает ошибка seg

2. Возможно, существует более одной ошибки. Вы должны запустить свою программу в отладчике и найти, где возникает ошибка. (В Linux вы можете использовать gdb и использовать bt для получения обратной трассировки при ее остановке.) Альтернативно, использование -fsanitize=address при компиляции программы также должно точно определять ошибку при запуске программы.

3. Я добавил к своему ответу.