1.
b那块内存,已经不被任何变量占用,属于不被管理的内存。b被回收,那块内存的内容并不会立刻被改写。
q is dirty after function'fun'!在执行完fun函数之后,q就变成了一个危险的指针。(p是二重指针,相当于&q,那么*p就相当于q了,是一个地址!)
2.(一道面试题,原博客地址http://blog.csdn.net/sdfgh2046/article/details/5647913)
总结:函数“地址传递”调用函数时必须用 函数名(&a)的方式。不加&就是值传递。指针也是变量,虽说他存储的是别人的地址。如果想把指针作地址传递,就必须使用地址的地址,即二重指针。
<pre name="code" class="cpp">#include<stdio.h>
#include<memory.h>
#include<string.h>
void Getmemery(char *p)
{
p=(char *)malloc(100); //其实此函数改变的是形参p,而非main函数中的str
} //要想在函数中改变str,就需要改变指向str(指针)的指针值,即二重指针的值!
void main()
{
char *str=NULL;
Getmemery(str);
strcpy(str,"helloworld");
printf("%s",str); free(str);
}
#include<stdio.h>
#include<memory.h>
#include<string.h>
char *Getmemery(void)
{
char *p=(char *)malloc(100);
return p;
}
void main()
{
char *str=NULL;
str = Getmemery();
strcpy(str,"helloworld");
printf("%s",str);
free(str);
}
这样代码执行的很正常,这种风格也是很常用的,也就作罢。 但是为什么第一个程序不正确?理论上,按照理论上分析是“正确”的,心里一直有个结,解不开这个题目,心里总是有点不舒服。
直到今天在ouravr上面看到有个关于 typedef用法的帖子,突然引起了我的兴趣,自己关于指针的,特别是二维数组和数组指针并不是理解的很透侧,于是就趁此机会好好的复习一下二维数组。
最后终于将之前的一个问题想明白了。
正确的代码如下:
#include<stdio.h>
#include<memory.h>
#include<string.h>
void Getmemery(char **p)
{
*p=(char *)malloc(100); //注意char *与*p是对应关系
}
void main()
{
char *str=NULL;
Getmemery(&str);
strcpy(str,"helloworld");
printf("%s",str);
free(str);
}
我们应该使用的是二重指针来完成任务。
分析一下之前的程序为什么会错了。
(1)void Getmemery(char *p)
(2)char *str=NULL;
(3)Getmemery(str);
1中子程序的形参是一个指针,然后很自然会想到2,3中的调用方式,本来的想法是用malloc分配内存,然后修改传入的指针变量,那么最后就根据通过
strcpy(str,"hello world");就可以向分配的内存里面写数据了。一切都是那样流畅,对,因为这个用法平时用习惯了,所以根本不会去考虑正确性。
然而,这里就出问题了。首先,Getmemery(str) 传递的是str指针的地址,这个没有问题,C不同于C++,参数是通过传递的,而不是通过引用。也就是说,实际参数 str先自己copy一份,然后传递给形式参数 *P接收,这个C语言的指针的时候已经强调多次了,但是自己还是错了啊,哈哈。
然后,在子程序里面,如果通过 *P 那么访问到的将是 *str的内容,这是等价的。但是,本程序一个致命的错误,非常隐蔽,那是子程序企图修改p(地址)的内容,而不是 *p(字符串数组)的内容!! 这个错误找了我很久终于给揪出来了。修改了 p的值是没有意义的,这个值是形式参数,并不会返回任何的东西,而 *p则是通过p的地址直接访问需要的变量,这是不同的用法。所以说白了,void Getmemery(char *p)执行之后并没有改变任何的东西,str的值并没有修改过,保持NULL,所以访问 *0 地址会被操作系统禁止,得到一个错误。
解决办法,是用2重指针。目的是要修改指针的地址,但是按照上面的分析,我们并不能去修改,但是我们可以用2重指针,将*str的地址值str,用2重指针来改变。
void Getmemery(void **p)
{
}
子程序修改为这个样子,出入的参数也得修改
char *str=NULL;
Getmemery(&str);
那么可以这样理解,因为形参是2重指针,所以 p (二重指针)对应 &str ,*p (指针,字符串数组首地址)对应str,之前说了,我们的目的是要修改 str的值,所以很自然,我们用 *p = xxx这样的形式去修改了。
为什么二重指针能够完成任务呢?(在调用函数的情况下)
想一想我们平时是怎样调用函数用指针的方式来改变实参的,不就是直接改变指针所指向的值么?(老谭的书例8.3,其实质不是传值,而是改变指针指向的值,*p(假设p是一个指针)),在这里要想改变str的值怎么办?str本身就是一个指针,“改变指针所指向的值”就变成了“改变指针的所指向的指针”,显然要传递的就是二重指针了!
简而言之,此例向我们展示了如何改变指针的值,即传递二重指针!(类比于改变一个变量的值要用一级指针的传递方式来理解)而在函数体中,指针的值就是*(二重指针变量)——剥掉一层。
如果我们没有用调用函数的方式,而是在主函数中直接就开辟空间给char*,就无须定义二重指针,如
#include<stdio.h>
#include<string.h>
#include<memory.h>
void main()
{
char*str=NULL;
str=(char*)malloc(100); //申请指针str所指向的块的大小,此空间在堆中,必须手动释放。
strcpy(str,"hello world");
printf("%s",str);
free(str);
}
这和建立动态链表的方法是一致的,如
struct student*p1,*p2;
p1=p2=(struct student*)malloc(sizeof(struct student ));
这实际上是和那个调用函数返回指针的方法是一致的,它们的实质就是申请指针str所指向的块的大小,即改变str的值!