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.实体作为参 ...
随机推荐
- Java进程占用CPU资源过多分析
问题描述: 生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析: 1,程序属于CPU密集型,和开发沟通 ...
- 一道JAVA经典面试题目的两种解法
题目要求:String s="-1 2 5 78 129 -65 -23";将字符串进行升序排序后输出. 方法一:使用数组进行排序 思路: 1.获取字符串中的数值: 2.将数组 ...
- cocos2d-x 详解之 CCTexture2D(纹理图片)和 CCTextureCache(纹理缓存)
精灵和动画都涉及到纹理图片的使用,所以在研究精灵与动画之前,我们先来了解一下纹理图片类CCTexture2D和纹理缓存CCTextureCache的原理: 当一张图片被加载到内存后,它是以纹理的形式存 ...
- dbms_file_transfer使用简介
dbms_file_transfer这个包可以在两个位置传输文件,分别可以有以下位置: a 从一个asm diskgroup传输到另外一个asm diskgroup b 从一个asm diskgrou ...
- Hadoop 2.6.0 POM.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- FLEX实现两侧边栏固定中间自适应布局
<style type="text/css"> #outer{ display: flex; width: 100%; flex-flow: row nowrap; } ...
- 【现代程序设计】【homework-05】
这次作业的运行效果图: 新建了20个客户端线程,服务器相应开了20个线程接收客户端数据,每一秒输出每一轮的结果 这次作业用c#完成 利用 Socket 类实现了局域网中的客户端和服务器之间的通信 主要 ...
- 最新的支持DELPHI XE6的开发框架
支持负载均衡集群的中间件 主界面 插件管理 角色与权限 用户与权限
- String中的Indexof,LastIndexOf, Indexofany,LastIndexOfAny 的区别
本文转载自 http://www.cnblogs.com/qinying/archive/2008/09/22/1295730.html 定位子串是指在一个字符串中寻找其中包含的子串或者某个字符.在S ...
- Java中的Filter过滤器
Filter简介 Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件 ...