指向指针的指针的作用

序:函数参数传递为什么要用指针:

在函数的参数传递中,编译器总是要为函数的每个参数制作临时副本,假如参数为p的话,那么编译器会产生p的副本_p,其实就是函数的形参,函数的形参是局部变量,当函数调用结束时,形参也没有了。那么我们想让实参发生改变时,如果不用指针,我们改变的只是形参,而无法改变实参。想要改变实参,就必须传指针。当我们的实参是普通值,如int val时,我们想改变它,就得传指向这个变量(val)的指针,即int * p;当我们传来的实参是int * p时,我们想改变指针变量(p)的值时,我们就得传指针的指针,即int ** pi。(实质:参数传递时,我们想改变这个参数,那么我们必须先得到这个参数的地址,再通过地址改变上面的值(中国古训:跑得了和尚,跑不了庙!))

1.指向指针的指针的作用?

源地址:http://zhidao.baidu.com/question/319831349.html

回答:如char** a就是指向指针的指针,其可以表示为一个二维数组,能通过a[0][0]来表示a所指向的位置。这个用得相当普遍

2.C语言建立动态链表要用双重指针吗?那个双重指针有什么作用?

http://zhidao.baidu.com/question/119156011.html
回答:
举个例吧,
假如一个结构:LinkNode
初始化:Initial(LinkNode **p);//这里就是双指针

那么在调用就这样,
LinkNode *p;
Initial(&p);

原理就是,如果你要通过函数来改变某个参数的值,那么就要传该参数的地址进去。
这里p是一个指针,所以要传指针的地址进去,也就是一个指向指针的指针;
初始化函数Initial(LinkNode *(*p));(*p)是指针,*(*p)就是传指针的地址进去了,意思就这样,不知道你听不听得懂。

不管动态还是静态,只要是要通过函数来改变指针类型数据的值,就要用双重指针。

3. 指向指针的指针与指针数组 

http://learn.akae.cn/media/ch23s06.html

指针可以指向基本类型,也可以指向复合类型,因此也可以指向另外一个指针变量,称为指向指针的指针。

int i;
int *pi = &i;
int **ppi = π

这样定义之后,表达式*ppipi的值,表达式**ppii的值。请读者自己画图理解ipippi这三个变量之间的关系。

很自然地,也可以定义指向“指向指针的指针”的指针,但是很少用到:

int ***p;

数组中的每个元素可以是基本类型,也可以复合类型,因此也可以是指针类型。例如定义一个数组a由10个元素组成,每个元素都是int *指针:

int *a[10];

这称为指针数组。int *a[10];int **pa;之间的关系类似于int a[10];int *pa;之间的关系:a是由一种元素组成的数组,pa则是指向这种元素的指针。所以,如果pa指向a的首元素:

int *a[10];
int **pa = &a[0];

pa[0]a[0]取的是同一个元素,唯一比原来复杂的地方在于这个元素是一个int *指针,而不是基本类型。

我们知道main函数的标准原型应该是int main(int argc, char *argv[]);argc是命令行参数的个数。而argv是一个指向指针的指针,为什么不是指针数组呢?因为前面讲过,函数原型中的[]表示指针而不表示数组,等价于char **argv。那为什么要写成char *argv[]而不写成char **argv呢?这样写给读代码的人提供了有用信息,argv不是指向单个指针,而是指向一个指针数组的首元素。数组中每个元素都是char *指针,指向一个命令行参数字符串。

例 23.2. 打印命令行参数

#include <stdio.h>

int main(int argc, char *argv[])
{
	int i;
	for(i = 0; i < argc; i++)
		printf("argv[%d]=%s\n", i, argv[i]);
	return 0;
}

编译执行:

$ gcc main.c
$ ./a.out a b c
argv[0]=./a.out
argv[1]=a
argv[2]=b
argv[3]=c
$ ln -s a.out printargv
$ ./printargv d e 
argv[0]=./printargv
argv[1]=d
argv[2]=e

注意程序名也算一个命令行参数,所以执行./a.out a b c这个命令时,argc是4,argv如下图所示:

图 23.4. argv指针数组

argv指针数组

由于argv[4]NULL,我们也可以这样循环遍历argv

for(i=0; argv[i] != NULL; i++)

NULL标识着argv的结尾,这个循环碰到NULL就结束,因而不会访问越界,这种用法很形象地称为Sentinel,NULL就像一个哨兵守卫着数组的边界。

在这个例子中我们还看到,如果给程序建立符号链接,然后通过符号链接运行这个程序,就可以得到不同的argv[0]。通常,程序会根据不同的命令行参数做不同的事情,例如ls -lls -R打印不同的文件列表,而有些程序会根据不同的argv[0]做不同的事情,例如专门针对嵌入式系统的开源项目Busybox,将各种Linux命令裁剪后集于一身,编译成一个可执行文件busybox,安装时将busybox程序拷到嵌入式系统的/bin目录下,同时在/bin/sbin/usr/bin/usr/sbin等目录下创建很多指向/bin/busybox的符号链接,命名为cplsmvifconfig等等,不管执行哪个命令其实最终都是在执行/bin/busybox,它会根据argv[0]来区分不同的命令。

4.指向指针的指针的作用

http://blog.csdn.net/hanchaoman/archive/2009/04/29/4137340.aspx

以下是经典程序(载自林锐的从c\c++高质量编程),讲解的部分是我个人理解

void GetMemory(char *p,int num)
{
p=(char*)malloc(sizeof(char)*num);       //p是形参指向的地址
}
void main()
{
char *str=NULL;
GetMemory(str,100);                            //str是实参指向的地址,不能通过调用函数来申请内存
strcpy(str,"hello");
}

结构是编译能通过,却不能运行,为什么呢?

先说一下指针作为函数参数的意义:当将指针作为参数时,实参向形参传递的是地址,在函数执行过程中,既可以对该参数指针进行处理,也可以对该参数指针所指向的数据进行处理,(以上程序段来说就是可以对p或*p进行处理)。由于此时形参和实参都是指向同一个存储单元,因此当形参指针所指向的数据改变时,实参指针所指向的数据也作相应的改变,因此这时的形参可以作为输出参数使用。

按照上面的说法,这个程序应该没有问题的啊,实参str和形参p指向同一个存储单元,给形参分配的内存单元应该也给实参分配了才对啊,问题就是在这里

实参和形参是指向同一个地址,它们只是指向相同,但它们自身的地址不是同时申请的,就是说p在申请内存时,str并不可以通过调用Getmemory同时申请内存,所以尽管str调用了GetMemory,但它仍然是个空指针,所以进行strcpy是就不能运行。

要使程序可以运行,只要小小的改动就行了(用指向指针的指针):

void GetMemory(char **p,int num)
{
*p=(char*)malloc(sizeof(char)*num);       //此时*p就变成了是形参本身的地址
}
void main()
{
char *str=NULL;
GetMemory(&str,100);                            //&str是实参的地址,所以实参和形参之间就可以直接调用
strcpy(str,"hello");
free(str);

程序就会如你所愿,输出hello了。

5.双指针的作用和用法(百度文库)

双指针多用于指针交换,可以避免数据复制,提升系统的性能,同时还可以让函数修改指针,例如扩充其大小,指向等

一般指针的指针用作参数,大多用在需要函数改变指针(重新引用变量)而又不能通过返回值传递(例如返回值用于传递其他结果)时。

为了能完全解决动态内存传递的问题,先回顾一下内存管理的知识要点.

(1)内存分配方式有三种:

●从静态存储区域分配。内存在程式编译的时候就已分配好,这块内存在程式的整个运行期间都存在。例如全局变量,static变量。

●在栈上创建。在执行函数时,函数内局部变量的存储单元都能够在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

 ●从堆上分配,亦称动态内存分配。程式在运行的时候用malloc或new申请任意多少的内存,程式员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用很灵活。

(2)指针的操作流程     

申请并初始化或配置为空:int*pInt=NULL;     

 开辟空间或使其指向对象:pInt=newInt(3);或inti=3;pint=&i;         

用指针(更确切地说是操作内存,在使用之前加if(pint!=NULL)或assert(pInt!=NULL)后再使用,以防内存申请失败的情况下使用指针):if(p!=NULL) {use pint};        

释放使用完的内存.free(pInt);       

置指针为空pInt=NULL;(避免野指针的出现)

(3)在函数的参数传递中,编译器总是要为函数的每个参数制作临时副本,假如参数为p的话,那么编译器会产生p的副本_p,使_p=p; 假如函数体内的程式修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针能够用作输出参数的原因.

例子1:

void  sample_alloc_safe_str_1(char   **lppsz,   unsigned   int  len)    

 {    *   lppsz   =   new   char[   len   +   1  ];     }        

 void  main()  

 {    

 char  *   lpsz   =   NULL;    

sample_alloc_safe_str_1(&lpsz,  100);        

Assert(lpsz  !=   NULL);   //   no   assert                   

 ...     }      

 /       

void  sample_alloc_safe_str_2(char   *lpsz,   unsigned   int  len)    

 {   

 lpsz  =   new   char[   len   +   1   ];   

}       

void  main()  

 {  

 char   *   lpsz   =   NULL;    

sample_alloc_safe_str_2(&lpsz,  100);      

 Assert(lpsz   !=   NULL);   //   assert!                     

...    }

例子2

voidfoo(int ** pp)

{// use **pp *pp=new int(10); } 这样就可以

int*p; // use p foo(&p);

如果不用指针的指针,那么就只能

 int*foo(int *p)

{// use *p return new int(10); }

 int*p; // use p

p=foo(p);

例子3

void AllocateInt(int *ptr, int len)

 {  int i;   ptr = (int *)malloc(len*sizeof(int)); }

AllocateInt(source, fsize);

上面函数AllocateInt()用一级指针,在函数体内申请内存,结果是AllocateInt(source, fsize);这样source的值永远不会被改变,因为一级指针是按值传递的,函数返回时栈内变量退出

如果用这种方式,可以改成如下:

int* AllocateInt(int len)

 { 

 int*ptr = (int *)

malloc(len*sizeof(int)); 

 if((*ptr)==NULL)

printf("memoryallocate error !! ");  

returnptr;  //堆内存,在函数返回时不会释放

}



  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值