Member 13037163 Ответов: 4

Вставка узла в двоичное дерево приводит к ошибке


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

Мне кажется, что оба фрагмента кода довольно хороши, но тогда почему первый не работает?

Поскольку мы просто разыменовываем указатель в более позднем случае, почему он не работает, когда я использую только указатель?


Что я уже пробовал:

Это тот, который работает (двойные указатели)

void Insert(struct bnode **root, int n){
    if(*root == 0){
        temp = (struct bnode *) malloc (sizeof(struct bnode));
        temp -> data = n;
        temp -> right = temp -> left = 0;
        *root = temp;
        return;
    }else{
        temp = *root;
        while(temp != 0){
            if(n >= temp -> data){
                temp = temp -> right;
            }else{
                temp = temp -> left;
            }
        }
        temp = (struct bnode *) malloc (sizeof(struct bnode));
        temp -> data = n;
        temp -> right = temp -> left = 0;
    }
}


Я хочу знать, почему приведенный ниже код не работает (указатели)

void Insert(struct bnode *root, int n){
    if(root == 0){
        temp = (struct bnode *) malloc (sizeof(struct bnode));
        temp -> data = n;
        temp -> right = temp -> left = 0;
        root = temp;
        return;
    }else{
        temp = root;
        while(temp != 0){
            if(n >= temp -> data){
                temp = temp -> right;
            }else{
                temp = temp -> left;
            }
        }
        temp = (struct bnode *) malloc (sizeof(struct bnode));
        temp -> data = n;
        temp -> right = temp -> left = 0;
    }
}

Member 13037163

P. S-Я знаю, что функциональность моего кода неверна, но мне просто нужно знать причину такого поведения.

Jochen Arndt

На этот вопрос нельзя ответить, не увидев полный код (как вызывается Insert () и с каким корневым аргументом).

Member 13037163

Вставить (корень, число);
В первом случае (указатель)
Вставить (& root, num);
В более позднем случае (двойной указатель)

Patrice T

Воспользуйся Улучшить вопрос чтобы обновить ваш вопрос.
Чтобы каждый мог обратить внимание на эту информацию.

4 Ответов

Рейтинг:
2

OriginalGriff

Компиляция не означает, что ваш код верен! :смеяться:
Подумайте о процессе разработки как о написании электронного письма: успешная компиляция означает, что вы написали письмо на правильном языке - например, на английском, а не на немецком, - а не то, что письмо содержало сообщение, которое вы хотели отправить.

Итак, теперь вы входите во вторую стадию разработки (на самом деле это четвертая или пятая, но к более ранним стадиям вы придете позже): тестирование и отладка.

Начните с рассмотрения того, что он делает и чем это отличается от того, что вы хотели. Это важно, потому что это дает вам информацию о том, почему он это делает. Например, если программа предназначена для того, чтобы позволить пользователю ввести число, а он удваивает его и печатает ответ, то если ввод / вывод был таким:

Input   Expected output    Actual output
  1            2                 1
  2            4                 4
  3            6                 9
  4            8                16
Тогда совершенно очевидно, что проблема заключается в бите, который удваивает его-он не прибавляет себя к себе или умножает его на 2, он умножает его на себя и возвращает квадрат входного сигнала.
Таким образом, вы можете посмотреть на код, и очевидно, что он где-то здесь:
private int Double(int value)
   {
   return value * value;
   }

Как только у вас появится идея, что может пойти не так, начните использовать отладчик, чтобы выяснить почему. Поставить точку останова на строке:
if(root == 0){

и запустите свое приложение. Подумайте о том, что должна делать каждая строка кода перед ее выполнением, и сравните это с тем, что она на самом деле делала, когда вы использовали кнопку "шаг вперед" для выполнения каждой строки по очереди. Он сделал то, что вы ожидали? Если да, то переходите к следующей строке.
Если нет, то почему? Чем это отличается?

Это навык, и его стоит развивать, поскольку он помогает вам как в реальном мире, так и в развитии. И, как и все навыки, он совершенствуется только при использовании!

Да, я, наверное, мог бы сказать вам, в чем "проблема" - но сделать это самому несложно, и в то же время вы узнаете что-то действительно стоящее!


Member 13037163

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

OriginalGriff

"ошибка произошла, когда я попытался распечатать данные в корневом узле."
Это мало что вам говорит - все, что это означает, это "значение было повреждено после того, как я вставил значение". Вам нужно знать, где он был поврежден - и шагнув в отладчик, вы узнаете об этом. Как только вы знаете, где, это должно быть довольно просто выяснить, почему!

Member 13037163

Я сделаю это и вернусь с результатами. Еще раз спасибо!

OriginalGriff

Пожалуйста!

Рейтинг:
2

Patrice T

Когда вы не понимаете, что делает ваш код или почему он делает то, что делает, ответ таков: отладчик.
Используйте отладчик, чтобы увидеть, что делает ваш код. Просто установите точку останова и посмотрите, как работает ваш код, отладчик позволяет вам выполнять строки 1 на 1 и проверять переменные по мере их выполнения, это невероятный инструмент обучения.

Отладчик-Википедия, свободная энциклопедия[^]
Освоение отладки в Visual Studio 2010 - руководство для начинающих[^]
Базовая отладка с помощью Visual Studio 2010-YouTube[^]

Отладчик здесь для того, чтобы показать вам, что делает ваш код, и ваша задача-сравнить его с тем, что он должен делать.
В отладчике нет никакой магии, он не находит ошибок, он просто помогает вам. Когда код не делает того, что ожидается, вы близки к ошибке.


Member 13037163

Спасибо за совет. Я обязательно этим займусь.

Patrice T

Для получения дополнительной помощи обновите свой вопрос полным кодом (достаточно для компиляции)

Member 13037163

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

Надеюсь, я прав насчет этого. Так Ли Это?

Patrice T

Не могу сказать, не видя фрагмента кода, который компилируется.

Рейтинг:
1

Jochen Arndt

Вы не показали нам полный код, но это должно быть что-то вроде

typedef struct bnode {
    int data;
    bnode *left;
    bnode *right;
};

bnode *root = NULL;

Затем используйте Insert(bnode **root) версия потому что только это обновит глобальную root здесь
*root = temp;
Версия с одним указателем назначит temp к локальной переменной root (переданный аргумент), но не обновляет глобальный.

Почему вы получили ошибку сегментации, невозможно ответить, не увидев полный код. Но я догадываюсь, что вы не проверяли, является ли ваш глобальный root до сих пор NULL после вызова Insert И это имеет место при использовании версии с одним указателем.


Member 13037163

Да, я понял, в чем проблема. Огромное спасибо.

Рейтинг:
0

CPallini

Проще говоря, C параметры всегда передаются по значению, поэтому, если вам нужно изменить значение внешней переменной внутри функции, то вы должны передать адрес такой переменной функции.
Так как вам нужно изменить значение root внутри Insert затем вы должны передать свой адрес, а именно &root. Это устанавливает правильную подпись Insert функция.
Попробуйте, например:

#include <stdio.h>
#include <stdlib.h>

void allocate_1(int * p)
{
  p = malloc(sizeof(int));
  *p = 1;
}

void allocate_2(int ** pp)
{
  *pp = malloc(sizeof(int));
  **pp = 2;
}

int main()
{
  int * p;


  // allocate_1
  p = NULL;

  allocate_1( p );

  printf("after allocate_1: p = %p", p);

  if ( p )
  {
    printf(", *p = %d", *p);
    free(p);
  }
  printf("\n");


  // allocate_2
  p = NULL;

  allocate_2( &p );

  printf("after allocate_2: p = %p", p);

  if ( p )
  {
    printf(", *p = %d", *p);
    free(p);
  }
  printf("\n");


  return 0;
}

выход:
after allocate_1: p = (nil)
after allocate_2: p = 0x13f7440, *p = 2


Member 13037163

Думаю, это ответ на мой вопрос. Большое спасибо!

CPallini

Добро пожаловать.