关于一级指针和二级指针作为参数的探究
在对二叉树的学习过程中,遇到了一个问题。问题的关键是创建二叉树函数的参数类型,这个参数是一级指针还是二级指针是症结所在。正确的是定义是参数是二级指针。
以下例子都是在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;
}
编译通过,执行过程如下图:
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;
}
经过编译,执行如下:
从执行过程中可以看到,创建前系统分配的根地址和创建后系统分配的根地址是一样的,即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系统下运行的结果如下:
因为tree没有初始化,其成员变量也是未初始化的,所以它的地址是未知的,tree.data也是未知的,而tree.next是未初始化的指针变量,在vc++的debug模式下自动初始化为0xcccccccc,这是个触发异常中断的地址。
如果我们把第print函数里的“print(root->next);”改为“printf("root->next:%p\n", root->next);”,把35-40行和52、53行代码解除注释,把54行注释掉,编译运行下,结果如下:
我们直接访问创建过程中系统分配的第一个地址和第二个地址,输出的data值就是我们创建过程中输入的值,而next域都为nil(无值),这是系统决定的,在windows下使用vc++编译运行的结果是
关于使用vc++编译运行程序会出现0xcccccccc和0xcdcdcdcd请参考:
http://blog.csdn.net/chenlycly/article/details/11711719
经过以上的整理,我对参数的值传递和地址传递有了更深的理解。以上是我个人的理解,如有不正确的地方,还望指正。
转载于:https://blog.51cto.com/slientradio/1392772