C++函数的传入参数是指针的指针(**)的详解
要修改变量的值,需要使用变量类型的指针作为参数或者变量的引用。如果变量是一般类型的变量,例如int,则需要使用int 类型的指针类型int *作为参数或者int的引用类型int&。但是如果变量类型是指针类型,例如char*,那么需要使用该类型的指针,即指向指针的指针类型 char* *,或者该类型的引用类型char*&。
首先要清楚 不管是指针还是值传入函数后都会创建一个副本,函数结束后值内容不能传出来是因为值的副本,而传入的值并没被修改,指针能传出来是因为我们修改的是指针指向的内容而不是指针指向的地址。
我们既然要举例子 就找一个比较经典的实用例子。
在我们进行内存管理的时候,如果想创建一个分配空间的函数,函数中调用了malloc方法申请一块内存区域。
先将一个错误的例子,如下:
void GetMemory1(char *p,int num)
{
p=malloc(sizeof(int)*num);
return;
}
void Test1()
{
char *p=NULL;
GetMemory(p);
}
上述例子 是很普通的 将一个指针作为参数来申请一个动态内存空间,可是这个程序是错误的。
错误的原因:
由于其中的*p实际上是Test1中p的一个副本,编译器总是要为函数的每个参数制作临时副本。在本例中,p申请了新的内存,只是把p所指向的内存地址改变了,但是Test1中p丝毫未变。因为函数GetMemory1没有返回值,因此Test1中p并不指向申请的那段内存。
因为malloc的工作机制是在堆中寻找一块可用内存区,返回指向被分配内存的指针。
所以这时p指向了这个申请的内存的地址。由于在指针作为传入参数的时候会在函数体中创建一个副本指针_p
_p指针和p指针的联系就是他们指向同一个内存区域,但是malloc的函数使得_p指向了另外一个内存区域,而这个内存区域并没有座位传出参数传给p,
所以p并没有发生任何改变,仍然为NULL。
如何才能解决上述问题呢?
使用下述办法可以解决问题:
void GetMemory2(char **p,int num)
{
* p=malloc(sizeof(int)*num);
return;
}
void Test2()
{
char *p=NULL;
GetMemory(&p);
}
下面开始分析GetMemory2()和 Test2()的原理:
char **p 可以进行拆分(从左向右拆分) char* *p ,所以可以认为*p是一个char *的指针(注意这里不是p而是*p)。那么p的内容就是一个指向char*的指针的地址,换句话来说p是指向这个char*指针的指针。
从test2可以看出 p是一个char*的指针, &p则是这个char*指针的地址,换句话来说 &p是指向这个char*p的指针的指针,与GetMemory2()定义相符。所以在调用时候要使用&p而不是p。
在GetMemory2()中 *p=malloc(sizeof(int)*num);
其中*p保存了 这个分配的内存的地址,那么p就是指向这个分配的内存地址的指针。
其实在为什么要用指针的指针的道理很简单:
因为VC内部机制是将函数的传入参数都做一个副本,如果我们传入的用来获取malloc分配内存地址的副本变化了,而我们的参数并不会同步,除非使用函数返回值的方式才能传出去。
所以我们就要找一个不变的可以并能用来获取malloc内存地址的参数,
如果能够创建另外一个指针A,这个指针指向GetMemory1(char * p,int ...)中的传入参数指针p,那么 就算*p的内容变了,而p的地址没变,我们仍然可以通过这个指针A来对应得到p的地址并获得*p所指向的分配的内存地址,没错这个指针A就是本文想要讲的指向(char *)指针的指针(char **)。
于是我们创建了一个char *的指针*p(注意这里不是char *的指针p),这个p作为传入参数,在进入函数后,系统会为该指针创建一个副本_p,我们让*_p指向malloc分配的内存的地址(注意这里是*_p而不是_p),_p作为指向这个分配的内存地址指针的指针,这样在分配过程中_p并没有变化。
另外注意在void Test2()中的char *p 的p是指向的是malloc分配的内存的地址,通过GetMemory2()函数后&p作为传入参数没有变化被返回回来,依据&p将p指针指向了真正分配的内存空间。
最后还要注意一个要点,void Test2()中 GetMemory(&p);和void GetMemory2(char **p,int num)这个函数的定义,很容易有一个迷惑,在使用处的变量和定义处的变量是如何一一对应的。
下面来做解释:
不对应关系:其实这两个p不是一个p,&p中的p是一个char *的指针,而char**p中的p却是指向char *指针的指针。
对应关系:其实这个对应关系就是 在void Test2()调用的时候传入参数&p是指向char *指针的指针,GetMemory2()定义中的char **p也是指向char*指针的指针,所以说 在调用时候传入的参数和在定义时候使用的传入参数必须是匹配的。
如果看了上面的文字还是比较混乱的话就用一句话来总结下重点:
1,VC的函数机制传入的参数都是会创建一个副本 不管是指针还是值;如果是值A则直接创建另外一个值B,值B拥有和A相同的值;如果是传入参指针C创建另外一个指针的副本D,那么D所指向的地址是和C相同的地址。
2,第1条总结可知指针传参比值传参的多出的功能是可以通过修改指针D所指向的地址的内容来讲函数的运算结果告知指针C,因为C和D指向相同的地址。
3,第2跳总结的指针C和D所共用的指向地址可以是值类型,也可以是另外一个指针的地址(也就是上面所讲的**)。
C++函数的传入参数是指针的指针(**)的详解的更多相关文章
- mysql存储过程或函数中传入参数与表字段名相同引发的悲剧
真实案例.如下的一个存储过程: create procedure Apple(in user_id int) begin delete from users where user_id = user_ ...
- C++之值传递&指针传递&引用传递详解
C++之值传递&指针传递&引用传递详解 目录 C++之值传递&指针传递&引用传递详解 1.函数基础 2.值传递 3.指针传递 4.引用传递 1.函数基础 一个函数由以下 ...
- WiFi无线网络参数 802.11a/b/g/n 详解
转载自:WiFi无线网络参数 802.11a/b/g/n 详解 如转载侵犯您的版权,请联系:2378264731@qq.com 802.11a/b/g/n,其实指的是无线网络协议,细分为802.11a ...
- Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量
Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程: 1.线程是一堆指令,是操作系统调度 ...
- [转载]C/C++可变参数之va_start和va_end使用详解
本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解. 介绍这两 ...
- C# 方法参数 out、ref、param 详解
ref和out都对函数参数采用引用传递形式——不管是值类型参数还是引用类型参数,并且定义函数和调用函数时都必须显示生命该参数为 ref/out形式.两者都可以使函数传回多个结果. ref 类似于 PH ...
- C#进阶系列——WebApi 接口参数不再困惑:传参详解
前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...
- WebApi 接口参数不再困惑:传参详解
阅读目录 一.get请求 1.基础类型参数 2.实体作为参数 3.数组作为参数 4.“怪异”的get请求 二.post请求 1.基础类型参数 2.实体作为参数 3.数组作为参数 4.后台发送请求参数的 ...
- 【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
原文地址:http://www.cnblogs.com/landeanfen/archive/2016/04/06/5337072.html 阅读目录 一.get请求 1.基础类型参数 2.实体作为参 ...
随机推荐
- SDUT 3571 Password 暴力搜索
这个题如果裸搜肯定超时了 但是我们可以枚举,用初始串的哪一位数字去填目标串的那一位数字 这样就是暴力6!,复杂度很低,然后需要解决过程中经过的点的问题, 因为是从左向右走,所以记录当前光标, 和当前达 ...
- <转>ERP的测试用例模板
1页面部分(1) 页面清单是否完整(是否已经将所需要的页面全部都列出来了)(2) 页面是否显示(在不同分辨率下页面是否存在,在不同浏览器版本中页面是是否显示)(3) 页面在窗口中的显示是否正确.美观( ...
- 在PC上测试移动端网站和模拟手机浏览器的5大方法
在PC上测试移动端网站和模拟手机浏览器的5大方法 来源:互联网 作者:佚名 时间:03-19 10:14:54 [大 中 小] 最近公司要开发网站的移动版,让我准备准备知 ...
- 通过gdb跟踪Linux内核装载和启动可执行程序过程
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的:通过对一个简单的可执 ...
- codevs1796-最小完全图
表示第一篇就是水题. 根据Prim的思想,我们可以证明:dis ( a , b ) > max { a b 最小生成树路径上的边权 } 把所有边sort一遍用并查集维护就可以了 #include ...
- c++ 概念及学习/c++ concept&learning(一)
学习过计算机组成原理就会知道,处理器会从主存中取得指令,然后进行解释执行.而他们的交流方式是以二进制方式进行的,也就是他们只能识别1和0 :其实计算机是不知道1和0的,现在的实现方式是以高电压与低电压 ...
- 如何用chrome修改js代码,跳过网站等待时间
用chrome修改js代码 By Z.H. Fu 切问录 [maplewizard.github.io](http://maplewizard.github.io ) 网页中大部分的限制都是由js编写 ...
- 在阿里云linux下使用SVN访问VisualSVN出错:SSL handshake failed: SSL error: Key usage violation in certificate has been detected
Subversion clients receive the following error message when attempting to connect to VisualSVN Serve ...
- 在linux中使用find
最近用到了在linux系统中查找文件这个任务,学习了下面两篇文章,尤其是过滤结果信息的小技巧.记录下来,省得忘了再找. http://www.360doc.com/content/10/1110/10 ...
- work_5
第五次作业对我个人来说是很难的,因为之前没怎么接触过这方面的内容,有幸能跟宗毅组成一队,我也仔细看了他的Python代码,因为对于Python也是第一次接触,所以我感觉在有限的时间里学会并且灵活运用还 ...