C中的二叉树 - 麻烦

时间:2010-10-06 19:27:08

标签: c data-structures binary-tree

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst* b, int i)
{
                b=malloc(sizeof(b));
                b->value=i;
                b->leftChild = NULL;
                b->rightChild = NULL;
                printf("[%i]",b->value);
                return;
}
main()
{
  bst* b;
  insert(b,5);
  printf("[%i]",b->value);
}

第二个printf会导致分段错误,将其注释掉它可以正常工作。我在这里做错了什么(忽略函数名的含义,即插入是bst的插入函数)?不应该在插入函数之外的设置中持续存在吗?

6 个答案:

答案 0 :(得分:4)

您需要通过引用传递指针以更改其指向的位置。

// just add this & here in the function definition.
// That's the only change in all your code (for c++)
void insert(bst * &b, int i)

或者在C中,指针指针

void insert(bst ** b, int i)
{
                *b=malloc(sizeof(*b));
                (*b)->value=i;
                (*b)->leftChild = NULL;
                (*b)->rightChild = NULL;
                printf("[%i]",(*b)->value);
                return;
}

请注意取消引用指针指针的更改。

当您调用此功能时,您需要获取bst *的地址:

insert(&b,5);

答案 1 :(得分:2)

问题是b中的main指向无效的内存。 b不会被insert更改,因为insert使用指针的副本。你必须有选择来解决这个问题:

  • 使“insert”获取指针指针
  • 在输入“insert”
  • 之前分配内存

因为第一个更难阅读(并理解),我更喜欢第二个。但这取决于你选择的变体。

insert之前的内存分配:

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst* b, int i)
{
                // No malloc here
                b->value=i;
                b->leftChild = NULL;
                b->rightChild = NULL;
                printf("[%i]",b->value);
                return;
}
main()
{
  bst* b = (bst *)malloc(sizeof(bst));  // This line has changed
  insert(b,5);
  printf("[%i]",b->value);
}

带有双指针的变体:

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst** b, int i)
{
                *b = (bst *)malloc(sizeof(bst));  // This line has changed
                (*b)->value=i;
                (*b)->leftChild = NULL;
                (*b)->rightChild = NULL;
                printf("[%i]", (*b)->value);
                return;
}
main()
{
  bst* b;
  insert(&b,5);  // Now b is changed
  printf("[%i]",b->value);
}

旁注1 :你的malloc()也不正确 - 你应该使用sizeof(bst)而不是sizeof(b)作为大小。这是因为sizeof(b) == sizeof(bst *) == size of an address,几乎肯定不是你想要的。

旁注2 :不要忘记在最后调用free(b)以避免内存泄漏。

旁注3 :检查malloc()是否未返回NULL。一个罕见的案例,但一个好的计划应该期待。

答案 2 :(得分:2)

由于C为参数传递值,因此insert()中引用的指针是原始b的副本。内存分配给函数内部的变量b,但不分配给main()函数中的变量。

例如。

# In main
b<main> = 0xOrigLoc
b<insert> = undefined

# In insert (before malloc)
b<main> = 0xOrigLoc
b<insert> = 0xOrigLoc

# After Malloc
b<main> = 0xOrigLoc
b<insert> = 0xNewLoc

请注意b<main>如何保持不变。你只是在函数内部调整了b的副本。

解决方案是使用指针指针,如前面的解决方案所述。

答案 3 :(得分:2)

不是更改值,而是返回b的新值

bst* insert(bst* b, int i)
{
    if (b == NULL)
    {
        bst* result = (bst*)malloc(sizeof(bst));
        result->value=i;
        result->leftChild = NULL;
        result->rightChild = NULL;
        printf("[%i]",b->value);
        return result;
    }

    if (i < b->value)
        b->left  = insert(b->left, i);
    else
        b->right = insert(b->right, i);
    return b;
}

int main()
{
  bst* b  = NULL;   // Initialise it to empty.
  b       = insert(b,5);
  printf("[%i]",b->value);
}

答案 4 :(得分:1)

一个问题是你没有对你的指针进行类型转换。

该行:

b=malloc(sizeof(b));

应该是:

b=(bst*)malloc(sizeof(b));

当我在gcc中编译它时,你的代码给我一个错误。


这是如何识别和修复未来的错误:

在GDB中进行测试

完整代码:

#include <malloc.h>
#include <stdio.h>

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst* b, int i)
{
  b=(bst*)malloc(sizeof(b));
                b->value=i;
                b->leftChild = NULL;
                b->rightChild = NULL;
                printf("[%i]",b->value);
                return;
}
main()
{
  bst* b;
  insert(b,5);
  printf("[%i]",b->value);
}

编译

  

%g ++ -g main.cc

随着

  

%gdb a.out

GDB命令:

b main.cc:211
run
print b
(output is: 0x0)
step
step
print b
(output is: 0x4f60010)
step
step
step
step
step
print b
(output is: 0x0)
quit

这清楚地表明当函数离开其范围时,您的变量将被释放。

将代码更改为:

#include <malloc.h>
#include <stdio.h>

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst* b, int i)
{
                b->value=i;
                b->leftChild = NULL;
                b->rightChild = NULL;
                printf("[%i]",b->value);
                return;
}
main()
{
  bst* b;
  b=(bst*)malloc(sizeof(bst));
  insert(b,5);
  printf("[%i]",b->value);
  free(b);
}

并使用以下命令重新运行gdb:

b main.cc:21
run
s
(short for step)
print b
(output: 0x1aab010)
s
s
s
s
s
s
print b
(output: 0x1aab010)

您的问题显然是分配的范围。将分配移至主要修复此问题。

你的程序现在可以使用了!

这可以让您深入了解如何使用调试器来解决这些类型的简单错误。


修改

正如一个操作指出的那样,你的根本问题是传递给c中函数的指针被视为本地对象,因此当你退出函数时,任何分配给单个指针的内存都会被丢弃在bitbucket中。

双指针允许您在函数内实际分配,您必须小心地根据需要取消引用它们。所以你的工作代码也可以是:

#include <malloc.h>
#include <stdio.h>

typedef struct node
{
    struct node *leftChild, *rightChild;
    int value;
} bst;

void insert(bst** b, int i)
{
                (*b)=(bst*)malloc(sizeof(bst));
                (*b)->value=i;
                (*b)->leftChild = NULL;
                (*b)->rightChild = NULL;
                printf("[%i]",(*b)->value);
                return;
}
main()
{
  bst* b;
  insert(&b,5);
  printf("[%i]",b->value);
  free(b);
}

答案 5 :(得分:-1)

bst * b;声明一个指针......但不分配实际的结构。 分配并初始化b,您将进入下一步。

这只是一种风格讨论,但你的主要应该看起来像这样。

main()
{
  bst *b;
  b = (bst*)malloc(sizeof(bst));
  /* more initialization */
  insert(b, 5);

  free(b);
}

或者像这样

main()
{
  bst *b;
  bst_new(&b);
  insert(b, 5);
  bst_free(&b);
}