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

简介: 要修改变量的值,需要使用变量类型的指针作为参数或者变量的引用。如果变量是一般类型的变量,例如int,则需要使用int 类型的指针类型int *作为参数或者int的引用类型int&。但是如果变量类型是指针类型,例如char*,那么需要使用该类型的指针,即指向指针的指针类型 char* *,或者该类型的引用类型char*&。

要修改变量的值,需要使用变量类型的指针作为参数或者变量的引用。如果变量是一般类型的变量,例如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所共用的指向地址可以是值类型,也可以是另外一个指针的地址(也就是上面所讲的**)。

相关文章
|
1天前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
35 2
|
1天前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
10 2
|
1天前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
6天前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
15天前
|
编译器 C++
【C++核心】函数的应用和提高详解
这篇文章详细讲解了C++函数的定义、调用、值传递、常见样式、声明、分文件编写以及函数提高的内容,包括函数默认参数、占位参数、重载等高级用法。
14 3
|
15天前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
16 3
|
1天前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`->`运算符,可以方便地访问和操作智能指针所指向的对象。
|
1月前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this->name`和`this->age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。
|
1月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称