C语言中二叉搜索树内存分配的分段错误

C语言中二叉搜索树内存分配的分段错误,c,segmentation-fault,malloc,binary-search-tree,nodes,C,Segmentation Fault,Malloc,Binary Search Tree,Nodes,我试图用C语言实现一个二叉搜索树,但在运行代码时遇到了一个分段错误。基本上,在我的代码中,我从一个文件中读取一行数据,为它创建一个节点,然后将其插入到我的二进制搜索树中。我甚至不确定我对这段代码的实现是否正确,因为我在尝试运行它时遇到了分段错误。我对C编程非常陌生,尤其是分配内存,所以我知道我的代码中可能有大量错误,甚至在代码本身的核心结构中。因此,如果您有助于查找分段错误背后的原因,或者有助于代码本身的核心结构,我们将不胜感激。您正在释放指向堆栈的指针: int main(int argc,

我试图用C语言实现一个二叉搜索树,但在运行代码时遇到了一个分段错误。基本上,在我的代码中,我从一个文件中读取一行数据,为它创建一个节点,然后将其插入到我的二进制搜索树中。我甚至不确定我对这段代码的实现是否正确,因为我在尝试运行它时遇到了分段错误。我对C编程非常陌生,尤其是分配内存,所以我知道我的代码中可能有大量错误,甚至在代码本身的核心结构中。因此,如果您有助于查找分段错误背后的原因,或者有助于代码本身的核心结构,我们将不胜感激。

您正在释放指向堆栈的指针:

int main(int argc, char *argv[]) {

bst_t root;                                       <<< root is allocated on stack
bst_t newNode;
int c;

while ((c = getchar()) != EOF){
  newNode = createNode();
  root = insertNode(&root,&newNode);
  }
  freeTree(&root);                                <<< Your are passing a pointer to root ( pointer to a stack element )
  return 0;
}



void freeTree(bst_t *parent){                    <<< parent is a pointer to a stack element
  if(! parent){
    return;
  }
  freeTree(parent->left);
  freeTree(parent->right);
  free(parent);                                  <<< you free a stack element => segmentation fault
}
malloc
函数返回指向新分配空间的指针,并将分配的大小作为参数

旁注:如果你真的想澄清这一点,
malloc
可以在没有可用内存(非常罕见)的情况下返回
null
,因此检查返回是一种很好的做法。

这可能更多的是一个评论而不是一个答案,但我没有足够的“声誉”来评论,所以

正如@Lundin之前所评论的,这个问题与指针的使用有关,而不是

在这方面:
*node->left=insertNode(node->left,newNode)


节点->左可以为空。在对insertNode的调用中,您检查此项并将newNode分配给node->left的本地副本,但这对node->left没有影响,因此当insertNode的返回值写入node left指向的位置(NULL)时,您一定会崩溃

我认为问题出在
readData
函数中。使用
scanf
时,必须使用运算符
的地址来读取变量

以下代码:

void readData(bst_t *node) {
while (scanf("%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],"
             "%[^,],%[^,],%[^,],%[^,],%[^,]",
             node->data.ID, node->data.name, node->data.sex,
             node->data.height, node->data.weight, node->data.team,
             node->data.NOC, node->data.games, node->data.year,
             node->data.season, node->data.city, node->data.sport,
             node->data.event, node->data.medal) == 14) {
    continue;
}
应改为:

void readData(bst_t *node) {
while (scanf("%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],"
             "%[^,],%[^,],%[^,],%[^,],%[^,]",
             &node->data.ID, &node->data.name, &node->data.sex,
             &node->data.height, &node->data.weight, &node->data.team,
             &node->data.NOC, &node->data.games, &node->data.year,
             &node->data.season, &node->data.city, &node->data.sport,
             &node->data.event, &node->data.medal) == 14) {
    continue;
}
还应进行一些修改:
1.使用指向结构而不是结构的指针传递参数和返回值。
2.处理链表时,在堆上而不是堆栈上分配内存(即使是根节点)。
3.在堆上分配内存时,请确保分配的大小是结构的大小,而不是指向结构的指针的大小。

4.确保释放堆上分配的所有内存,不要像这里所做的那样错误地释放堆栈上分配的内存

您的代码中存在多个问题:

  • root
    main
    函数中未初始化。在将其传递给
    insertNode()
    之前,必须将其初始化为
    NULL

  • 您应该只分析
    readdata()
    中的一行,并返回一个成功指示器

  • 您应该在
    main
    循环中迭代,直到
    readdata
    无法从文件中读取更多记录。您当前的测试
    而((c=getchar())!=EOF)
    是不合适的,并且会破坏此输入流

以下是
readdata
main
功能的修改版本:

bst_t *createNode(void) {
    bst_t *newNode;
    newNode = (struct bst_t *)malloc(sizeof(struct bst_t));
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

int readData(bst_t *node) {
    return scanf("%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],"
                 "%[^,],%[^,],%[^,],%[^,],%[^,]",
                 node->data.ID, node->data.name, node->data.sex,
                 node->data.height, node->data.weight, node->data.team,
                 node->data.NOC, node->data.games, node->data.year,
                 node->data.season, node->data.city, node->data.sport,
                 node->data.event, node->data.medal) == 14;
}

int main(int argc, char *argv[]) {
    bst_t *root = NULL;
    bst_t *newNode;

    while ((newNode = createNode()) != NULL) {
        if (readData(newNode)) {
            /* record was read correctly: insert into the tree */
            root = insertNode(root, newNode);
        } else {
            /* end of file or input error: stop reading the file */
            free(newNode);
            break;
        }
    }
    freeTree(root);
    return 0;
}

createNode()
中,分配的大小应该是
sizeof(struct bst)
,而不是指针的大小。此外,不应按值返回结构。通过指针返回它们,或者使用
Return
,或者(最常见的)通过其中一个参数返回。我知道这是如何工作的,但是更改它仍然会给我一个分段错误readData做什么?你能提供它的定义吗?我已经更新了主postHi,谢谢你的帮助,尽管我有点困惑。我了解如何使用指向结构而不是实际结构的指针,但我不太确定我在哪里没有这样做?(我对指针的理解水平不高)。另外,在堆和堆栈方面,我想我理解这些概念,但是它们是从哪里进入程序的呢?就像我如何在堆而不是堆栈上分配和释放内存一样。cheers函数
createNode
insertNode
返回的是结构,而不是指向结构的指针。当使用malloc、calloc或realloc分配内存时,就是在堆上分配内存。当您只声明一个变量(而不是指向它的指针)时,您将该变量放在堆栈上。例如:
int*i;i=(int*)malloc(sizeof(int))//堆分配
intj//堆栈分配
因此将createNode中的声明更改为
bst\u t*newNode
newNode=(struct bst_t*)malloc(sizeof(struct bst_t))修复此问题?函数返回的内容在函数之前
bst\u t insertNode(bst\u t*node,bst\u t*newNode)
应该是
bst\u t*insertNode(bst\u t*node,bst\u t*newNode)
。当然,你在评论中所说的也是正确的。好的,我想我现在理解了这一点,并在代码中修复了它。然而,显然代码中还有更多错误导致分段错误,假设分配现在是正确的,那么是释放导致了问题,那么我应该如何在这里释放堆而不是堆栈?我应该如何分配根注释以便在这里释放它?对不起,我对指针和分配内存的理解不是很好,所以我是否可以通过使用指针将newNode分配给node->left的实际副本来解决这个问题?像这样重新定义insertNode可能是正确的一步:void insertNode(bst_t**node,bst_t*newNode)。这样,您就可以处理可能为空的实际指针。由于您在代码中混合传递指针和值,因此它是不同的
bst_t *createNode(void) {
    bst_t *newNode;
    newNode = (struct bst_t *)malloc(sizeof(struct bst_t));
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

int readData(bst_t *node) {
    return scanf("%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],"
                 "%[^,],%[^,],%[^,],%[^,],%[^,]",
                 node->data.ID, node->data.name, node->data.sex,
                 node->data.height, node->data.weight, node->data.team,
                 node->data.NOC, node->data.games, node->data.year,
                 node->data.season, node->data.city, node->data.sport,
                 node->data.event, node->data.medal) == 14;
}

int main(int argc, char *argv[]) {
    bst_t *root = NULL;
    bst_t *newNode;

    while ((newNode = createNode()) != NULL) {
        if (readData(newNode)) {
            /* record was read correctly: insert into the tree */
            root = insertNode(root, newNode);
        } else {
            /* end of file or input error: stop reading the file */
            free(newNode);
            break;
        }
    }
    freeTree(root);
    return 0;
}