C程序正在接受一个分支,即使它不应该';T
我已经写了一个C程序,它从一个数组构造一个二叉搜索树。它将经历以下步骤: 1:使用C程序正在接受一个分支,即使它不应该';T,c,arrays,segmentation-fault,binary-tree,binary-search-tree,C,Arrays,Segmentation Fault,Binary Tree,Binary Search Tree,我已经写了一个C程序,它从一个数组构造一个二叉搜索树。它将经历以下步骤: 1:使用qsort()对数组进行排序 2:使用递归函数treeify(),将数组的已排序元素放入二叉树中: 2a:取数组的中间元素(将其长度除以2),并将其作为树结构(此子树的根节点)的content字段 2b:然后,函数将剩余元素的左半部和右半部复制到更小的数组中,并分别为每个数组调用自己 2c:通过根节点返回树 3:递归遍历树并以缩进格式打印其内容 基本上,我使用了一个分而治之的范例来从已经排序的数组构建树。令人惊讶的
qsort()
对数组进行排序
2:使用递归函数treeify()
,将数组的已排序元素放入二叉树中:
2a:取数组的中间元素(将其长度除以2),并将其作为树结构(此子树的根节点)的content
字段
2b:然后,函数将剩余元素的左半部和右半部复制到更小的数组中,并分别为每个数组调用自己
2c:通过根节点返回树
3:递归遍历树并以缩进格式打印其内容
基本上,我使用了一个分而治之的范例来从已经排序的数组构建树。令人惊讶的是(因为这是我第一次设计D&C算法),这部分进行得相当顺利
我真正遇到麻烦的地方是在第三步。有时它是有效的,当它起作用时,所有的元素都是按正确的顺序排列的,所以这一部分显然是有效的。但是90%的时候我运行程序,当它到达第一个叶节点时,它会出现故障
以下是完整的程序文本。我修改了printing函数,以便它打印节点的地址(用于调试)。最初它显示的是数值
#include <stdio.h>
#include <stdlib.h>
struct tree {
int content;
struct tree *left;
struct tree *right;
};
struct tree *treeify( int *, size_t );
void printtree( struct tree *, int );
int comp( int *, int * );
int main( int argc, char **argv ){
int array[] = { 5, 6, 7, 2, 3, 4, 9, 1, 8, 0 };
/* Sort array */
qsort( (void *) array, 10, sizeof( int ), (int (*)(const void *, const void *)) &comp );
for( int i = 0; i < 10; i++ ){
printf( "%d ", array[i] );
}
printf( "\n" );
/* Treeify array */
struct tree *rootnode = treeify( array, 10 );
/* Print tree */
printtree( rootnode, 0 );
return 0;
}
// Place sorted array elements in a tree
// Function is called for each subtree
struct tree *treeify( int *array, size_t size ){
struct tree *root = (struct tree *) malloc( sizeof( struct tree ) );
size_t middle = size/2;
int leftsize = middle, rightsize = size-middle-1;
int left[leftsize], right[rightsize];
for( int i = 0; i < leftsize; i++ ) left[i] = array[i];
for( int i = 0; i < rightsize; i++ ) right[i] = array[i+middle+1];
root->content = array[middle];
if( leftsize > 0 ) root->left = treeify( left, leftsize );
if( rightsize > 0 ) root->right = treeify( right, rightsize );
return root;
}
// Print tree contents in indented format
void printtree( struct tree *node, int level ){
for( int i = 0; i < level; i++ ) printf( " " );
printf( "%x\n", &(node->content) );
if( node->left ) printtree( node->left, level+1 );
if( node->right ) printtree( node->right, level+1 );
}
// Comparison function for qsort
int comp( int *xp, int *yp ){
int x = *xp, y = *yp;
if( x < y ) return -1;
if( x > y ) return 1;
return 0;
}
和一次不成功的跑步:
0 1 2 3 4 5 6 7 8 9
cbe00000
cbe00020
cbe00040
cbe00060
cbe00080
cbe000a0
cbe000c0
cbe000e0
cbe00100
cbe00120
f04032b0
f04032d0
f04032f0
f0403310
0
Segmentation fault: 11
请注意,在返回和返回之前,成功的运行只经过树的三个级别。不成功的运行经过四个级别,到达一个空指针
具体来说,当程序到达这一行时:
if( node->left ) printtree( node->left, level+1 );
它将分支节点->左求值设置为零(如输出的第五行所示)
这是我一生都无法理解的。条件显然是评估为false(我已经验证了这一点),但程序仍然将该分支视为true(并且仅在大多数情况下,而不是所有情况下)
这在我身上从未发生过。我需要一个比我更了解C的人来为我解释这一点
我能想到的唯一可能性是:
- 一些奇怪的编译器优化
- 我在某处犯了一个愚蠢的单字符错误
- 我的CPU部分烧坏了
问题在于,您试图从结构的未初始化成员中读取数据,但第一次发生的情况就是在这里
if (node->left)
在printtree()函数中
未初始化的值是这样保存的,尝试读取它们是未定义的行为,所以这就是为什么程序的行为并不总是相同的
您需要初始化这两个成员,事实上最好是
struct tree *create_node(int content)
{
struct tree *node;
node = malloc(sizeof(*node));
if (node == NULL)
return NULL;
node->content = content;
node->left = NULL;
node->right = NULL;
return node;
}
你也应该
避免使用malloc()
在使用指针之前,请检查malloc()
是否未返回NULL
对于启动器,功能comp
应声明如下
int comp( const void *, const void * );
其次,在函数treefy
中,当leftsize
或rightsize
等于0时,数据成员right
的数据成员left
将具有不确定值
无需使用辅助数组,该函数可以更简单地实现
struct tree * treeify( const int *array, size_t size )
{
struct tree *node = NULL;
if ( size )
{
node = malloc( sizeof( struct tree ) );
size_t middle = size / 2;
node->content = array[middle];
node->left = treeify( array, middle );
node->right = treeify( array + middle + 1, size - middle - 1 );
}
return node;
}
函数printtree(
错误。例如,它不检查第一个参数是否等于NULL
这是一个演示程序
#include <stdio.h>
#include <stdlib.h>
struct tree
{
int content;
struct tree *left;
struct tree *right;
};
int comp( const void *, const void * );
struct tree * treeify( const int *, size_t );
void printtree( const struct tree *, int level );
int main(void)
{
int array[] = { 5, 6, 7, 2, 3, 4, 9, 1, 8, 0 };
const size_t N = sizeof( array ) / sizeof( *array );
qsort( array, N, sizeof( *array ), comp );
for ( size_t i = 0; i < N; i++ ) printf( "%d ", array[i] );
putchar( '\n' );
struct tree *rootnode = treeify( array, N );
printtree( rootnode, 0 );
return 0;
}
int comp( const void *left, const void *right )
{
int x = *( const int * )left;
int y = *( const int * )right;
return ( y < x ) - ( x < y );
}
struct tree * treeify( const int *array, size_t size )
{
struct tree *node = NULL;
if ( size )
{
node = malloc( sizeof( struct tree ) );
size_t middle = size / 2;
node->content = array[middle];
node->left = treeify( array, middle );
node->right = treeify( array + middle + 1, size - middle - 1 );
}
return node;
}
void printtree( const struct tree *node, int level )
{
if ( node )
{
printf( "%*s", level, "" );
printf( "%d\n", node->content );
if( node->left ) printtree( node->left, level + 1 );
if( node->right ) printtree( node->right, level + 1 );
}
}
“两种行为”是指一种称为“未定义行为”的行为。请尝试在下运行代码,并检查是否存在任何无效的读写操作。分配新的树时,您不会设置left=right=NULL
。是的,解决方案是只需将left
和right
初始化为NULL
,就没有单一行为NULL
在您的代码中。在&comp
前面的强制转换标志着您应该更改comp
函数的原型以符合要求。是的,这现在是有意义的。而且,我意识到我正在打印的值不是if
语句所评估的值。我正在打印&(节点->内容)
而条件是计算节点
。这就是为什么即使地址为NULL
,它似乎也会执行分支。是的,使用指针算术比复制数组更有意义。出于某种原因,我认为这只适用于具有NULL终止符的字符串,但由于长度数组的属性作为参数给出,这应该足够了。我更喜欢你的comp()
函数。比if
子句优雅得多。
0 1 2 3 4 5 6 7 8 9
5
2
1
0
4
3
8
7
6
9