#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-ofamp;
?
Ответ №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. Я добавил к своему ответу.