在对二叉树的学习过程中,遇到了一个问题。问题的关键是创建二叉树函数的参数类型,这个参数是一级指针还是二级指针是症结所在。正确的是定义是参数是二级指针。

   以下例子都是在ubuntu 12.04上编辑、编译和执行。

   二叉树的存储结构使用链式结构,节点(node)使用一个数据结构如下:

typedef struct node
{
    int data;
    struct node * next;
}btree;

   首先,以一个例子进行讲解,下面是一段程序代码:

#include <stdio.h>
#include <stdlib.h>
 int i = 0;
 int count = 0;
typedef struct node
{
    int data;
    struct node * next;
}btree;
btree * ptr1 = NULL;
btree * ptr2 = NULL;
void print(btree *root)
{
    if(root != NULL)
    {
        i++;
        printf("访问的第%d个地址:%p\n",i , root); //print访问的地址
        printf("data :  %d\n", root->data);
        print(root->next);
    }
    else
        printf("null\n");
}
void create(btree **root)
{
    int ii;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    scanf("%d", &ii);
    if(ii == 0)
        *root = NULL;
    else{
        *root = (btree*)malloc(sizeof(btree));
        count++;
        printf("创建过程中分配的第%d个地址:%p\n",count, *root);
        (*root)->data = ii;
        create(&(*root)->next);
    }
}
int main()
{
    btree *tree;
    printf("创建前系统分配的根地址 : %p\n", tree);
    create(&tree);
    printf("创建后系统分配的根地址 : %p\n", tree);
    print(tree);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    return 0;
}

   编译通过,执行过程如下图:

wKiom1NHXtmTqrzNAAInX9YWGCg848.jpg

   12、34、56、78和0都是输入的数据,从执行过程中可以看到,创建前系统分配的根地址和创建后系统分配的根地址是不一样的,即tree的地址发生了变化。因为在create函数中

   *root = (btree*)malloc(sizeof(btree));

   root原来存放的是tree的旧地址,现在给tree的地址重新赋值,即tree的地址发生变化,tree的数据放在一块新的内存空间。连续给next开辟内存空间,即新的节点的地址,第2个地址,第3个地址……

   print函数打印输出每个节点的地址和data值。可以看到当问的第一个(节点)地址和创建后系统分配的跟地址是一样的,创建的节点地址和访问的节点地址也都是一样的。

   这种create函数的参数是二级指针的定义是创建二叉树常用的,是正确的。而我用了另外一种定义,虽然是错误的,但我却一直没有明白为什么这样行不通。

   下面是我的另一种错误的定义:

#include <stdio.h>
#include <stdlib.h>
 int i = 0;
 int j = 0;
 int count = 0;
typedef struct node
{
    int data;
    struct node * next;
}btree;
btree * ptr1 = NULL;
btree * ptr2 = NULL;
void print(btree *root)
{
    if(root != NULL)
    {
        i++;
        printf("访问的第%d个地址:%p\n",i , root);
        printf("data :  %d\n", root->data);
        print(root->next);
    }
    else
        printf("null\n");
}
void create(btree *root)
{
    int ii;
    scanf("%d", &ii);
    if(ii == 0)
        root = NULL;
    else{
        root = (btree*)malloc(sizeof(btree));
        count++;
    /*  j++;
        if(j == 1)
            ptr1 = root;
        else if(j== 2)
            ptr2 = root;
    */
        printf("创建过程中分配的第%d个地址:%p\n",count, root);
        root->data = ii;
        create(root->next);
    }
}
int main()
{
    btree tree;
    printf("创建前系统分配的根地址 : %p\n", &tree);
    create(&tree);
    printf("创建后系统分配的根地址 : %p\n", &tree);
//  print(ptr1);
//  print(ptr2);
    print(&tree);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
        return 0;
}

   经过编译,执行如下:

wKioL1NHX6_guBZCAAKQViri7n8611.jpg

   从执行过程中可以看到,创建前系统分配的根地址和创建后系统分配的根地址是一样的,即tree的地址没有发生变化,说明create函数并没有改变tree的地址。传入的参数root存放的是tree的地址,即它指向tree。在create函数里:

       root = (btree*)malloc(sizeof(btree));

   使root存放的是另一个新的地址,这个地址是系统分配的,我们不知它指向的单元内容是什么。这样root和tree不再有任何关系。我们也不知道tree->next的地址,因为系统并没有给它指定分配内存空间,它是未知的。

   而调用print函数,传入的参数是tree的地址,tree->next的地址是未知的,因而访问的第2-5个地址都是没有规律的,而它有可能指向系统的地址空间,系统是不允许访问系统的内存单元的,因而系统会阻止这种访问,程序停止运行,出现错误Segmentation fault (core dumped),在windows系统下运行的结果如下:

wKiom1NHYcuCZgFmAAIUvRXBbwM590.jpg

   因为tree没有初始化,其成员变量也是未初始化的,所以它的地址是未知的,tree.data也是未知的,而tree.next是未初始化的指针变量,在vc++的debug模式下自动初始化为0xcccccccc,这是个触发异常中断的地址。

如果我们把第print函数里的“print(root->next);”改为“printf("root->next:%p\n", root->next);”,把35-40行和52、53行代码解除注释,把54行注释掉,编译运行下,结果如下:

wKioL1NHcBuyCwsDAAHqZXkKlgo363.jpg

   我们直接访问创建过程中系统分配的第一个地址和第二个地址,输出的data值就是我们创建过程中输入的值,而next域都为nil(无值),这是系统决定的,在windows下使用vc++编译运行的结果是

wKioL1NHclKhoZtHAAHILW5wB6M585.jpg

   关于使用vc++编译运行程序会出现0xcccccccc和0xcdcdcdcd请参考:

   http://blog.csdn.net/chenlycly/article/details/11711719

   经过以上的整理,我对参数的值传递和地址传递有了更深的理解。以上是我个人的理解,如有不正确的地方,还望指正。