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