C++函数的传入参数是指针的指针(**)的详解

本文转载至:Jessica程序猿 博文 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* 的指针的指针,与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 相同的地址(C 和 D 本身地址不同,是两个不同的变量,注意编译器本身会为指针创建地址,只是这个地址我们很难访问)。
  2. 根据第 1 条可知指针传参比值传参的多出的功能是,可以通过修改指针 D 所指向的地址的内容来将函数的运算结果告知指针 C,因为 C 和 D 指向相同的地址。(实际上修改的不是 C 和 D,是 D 所指向的地址,而 C 正好也指向这个地址。)
  3. 根据第 2 条 可知指针 C 和 D 所共用的指向地址可以是值类型,也可以是另外一个指针的地址(也就是上面所讲的**)。(因此如果我们传入的参数是指针,而在函数内对指针的修改要作用于这个地址上的话,就需要再创一个指针指向这个参数指针)

原文链接: https://www.cnblogs.com/cscshi/p/15765490.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++函数的传入参数是指针的指针(**)的详解

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/183510

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 上午10:24
下一篇 2023年2月12日 上午10:24

相关推荐