指针好像一直都是一个头疼的问题,从一级指针到二级指针的运用,总是经常出错。今天遇到的一个二级指针的问题,花了很多时间,想了一个比较合理的解释。
今天在数据结构的实验中,用一个二级指针作为参数来写了一个二叉树的建立。大概的函数代码如下:
存储结构如下:
/*树的存储结构*/
typedef struct Tree
{
char data;
struct Tree *Lift;
struct Tree *Right;
}Tree,*Btree;
/*二级指针建立二叉树*/
void crea(Btree *root)
{
printf("%p\n",root);
char ch;
ch = getchar();
if(ch=='#')
{
(*root) = NULL;
}
else
{
*root=(Tree *)malloc(sizeof(Tree));
(*root)->data = ch;
// printf("ch=%ckk\n",ch);
if((*root)==NULL)
{
exit(0);
}
crea((&(*root)->Right));
crea((&(*root)->Lift));
}
}
然后为了测试这个创建二叉树的函数对不对,我在主函数里,简单地定义了一个二级指针,给传了进去,然后问题来了。
int main()
{
Btree *p;
crea(p);
}
然后程序,跑到malloc的时候就断错误了。
*root=(Tree *)malloc(sizeof(Tree));
printf("%c\n",ch);//该输出是输出不了的,以段错误结束
然后改了下主函数传递参数的方式,就能够正常的malloc了。
int main()
{
Btree p; //一级指针
crea(&p); //传入一级指针本身的地址
}
同样相当于二级指针,但是为什么第一种就会出现断错误。主要原因是malloc一块明确的空间后要给一个明确的地址。
最简单的列子(首先搞懂自定义函数形参怎么传递的)
#include<stdio.h>
#include<stdlib.h>
int fun(int a)
{
a++;
return a;
}
int main()
{
int b = 0;
int a = 10;
fun(a);
b = fun(b);
printf("b=%d\n",b);
printf("a=%d\n",a);
return 0;
}
在C语言中,函数的参数传递是值传递,素以fun(a),只是把a的值传递给了自定义函数中的a,然后a在自定义函数中自加,但是主函数里面的a是不变的,只是多了return,return会把a自加的结果赋值给主函数中的b。结果如下:
但是这个传递给这个自定义函数的是一个主函数里a变量的地址,那么在自定义函数中就会对这个a进行改变。
#include<stdio.h>
#include<stdlib.h>
int fun(int *a)
{
(*a)++;
return *a;
}
int main()
{
int b = 0;
int a = 10;
fun(&a);
b = fun(&b);
printf("b=%d\n",b);
printf("a=%d\n",a);
return 0;
}
第二个代码中,在形参定义的是一个指向int类型的指针,而从主函数中传入的是a变量的地址,那么形参将获得主函数中a的地址,然后直接在a的地址上操作其内容。而第一个代码中,形参只是获得了a的数值,并没有获得其本身的地址,所以没有对主函数中的变量a进行改变的权利。第二个代码中,传递的值是a的地址值,直接从地址值对a进行了改变。
然后理解中的指针(有它自己的地址值,和存储的区域)
地址为100 | 地址为200 |
---|---|
存储的内容 | 存储的内容 |
所以我理解的二级指针(**p)大概就是:
100 | 104 | 108 |
---|---|---|
存地址104 | 存地址108 | 存一个a |
所以一级指针便是(*p):
100 | 104 |
---|---|
存地址104 | a |
最后回到原来的错误代码
int main()
{
Btree *p;
crea(p);
}
其中p是一个二级指针,因为Btree是*Btree。此时传入的是二级指针,是一个指针变量,并没有明确地指向某个空间。在自定义函数creat接收它之后,root指向了p,然后malloc开辟了一个明确的空间,接下来root将指向这个随机空间的首地址,但是在此之前,root指向了p,而p又没有指向一个明确的空间,所以此时的malloc会出错。
int main()
{
Btree p;
crea(&p);
}
第二个代码中,p是一个一级指针变量,但是传入的时候&p,此时假如p指针本身的地址是100,那么就是将100传给了变量root,root是一个二级指针,是存储指针的指针变量,此时将100存入了root存储内容中,malloc一块空间后,再降root存储的这个地址的指针再指向malloc开辟的空间。此时malloc赋给了一个明确的指针。所以不会出错。
再举一个简单的类比例子
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *p;
*p = 9;
printf("p=%d\n",*p);
}
会出现断错误,因为*p可以存内容为9的地址,但是不能直接降9赋给它,因为此时P没有明确地指向一个空间。可以用malloc给它指向一个明确的空间后,才能降9存入到其中。如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *p;
p = (int *)malloc(4);
*p = 9;
printf("p=%d\n",*p);
}
或者这样:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 9;//申请一个地址,初始化为9.
int *p;
//p = (int *)malloc(4);
p = &a;
printf("p=%d\n",*p);
}
所以今天的代码还可以做这样的改正。
int main()
{
Btree *p;
Tree *q=NULL; //申请了一个地址,代号为p,初始化为NULL
p = &q; //p明确存储了q的地址,因此**p传入之后有了明确指向
crea(p); //所以成功传入并没有发生错误
}
感觉自己对指针的领悟还有待提升,如果这次这个解释存在错误的话,希望大家可以帮我指出。