Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C程序正在接受一个分支,即使它不应该';T_C_Arrays_Segmentation Fault_Binary Tree_Binary Search Tree - Fatal编程技术网

C程序正在接受一个分支,即使它不应该';T

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:递归遍历树并以缩进格式打印其内容 基本上,我使用了一个分而治之的范例来从已经排序的数组构建树。令人惊讶的

我已经写了一个C程序,它从一个数组构造一个二叉搜索树。它将经历以下步骤:

1:使用
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