【重要!】一文理解指针作为函数参数传递!看完不懂你打我

先说最重要的结论

当指针作为参数传入函数时,是值传递而非引用传递!!!

其次,不要把指针在定义时和使用时搞混!

定义时/值 p *p

**p

int p;//变量 int变量
int *p;//一重指针 int变量的地址 int变量
int **p;//二重指针 int变量的地址的这块内存的地址 int变量的地址 int变量
void function(int **p)//参数是一个二重指针 int变量的地址的这块内存的地址 int变量的地址 int变量

举个例子:

定义了函数:

void function(int **p)

大家可能会去想:呀,这个参数是个双指针,**p代表一个具体的int值,所以在使用的时候,可能会以为这样使用:

int a=10;
function(a);

错!

因为在定义时,**p代表这是一个双指针,这是一个地址的地址

而在使用时,**P才代表了指向变量本身的数值

再写简单一点:

int a=10;
int *p=&a;
int **pp=&p;

定义时:**pp=&p=指针p的地址

使用时:**pp=a=10;

这个虽然简单,但是在理解上会带来非常多的困难,切忌不能搞混,搞混了就回来看看!

通过一段简单的代码理解指针作为参数传递(指针实参和形参)

#include <bits/stdc++.h>
using namespace std;

void test(int *b)
{

cout << "形参指针地址:" << &b << endl;
cout << "形参指向内容:" << *b << endl;
cout << "实参和形参指向同一地址,但实参指针变量和形参指针变量自身地址不一致,可以看出传参方式为值传递而并非引用传递" << endl;
cout << endl;
cout << "==更改后==" << endl;
int num2 = 2;
b = &num2;
cout << "型参指向内容:" << *b << endl;
}

int main()
{
int num = 1;
int *a = &num;

cout << "==更改前==" << endl;

cout << "实参指针地址:" << &a << endl;
cout << "实参指向内容:" << *a << endl;
test(a);

cout << "实参指向内容:" << *a << endl;

return 0;
}

运行结果:

==更改前==
实参和形参指向同一地址,但实参指针变量和形参指针变量自身地址不一致,可以看出为值传参而并非引用传参
实参指针地址:0x61fe10
形参指针地址:0x61fdf0
==更改后==
型参指向内容:2
实参指向内容:1

简而言之:指针a和指针b在一开始指向同一变量,但指针a和指针b自身所在的地址不同。

在test函数中,指针b指向了其他的变量,所以修改内容没有同步到指针a指向的变量

即test函数参数传递为值传递

如果想要更加详细的理解,参考博客:很好的指针传参理解博客

下面通过1161二叉树遍历的题目来看实际中的应用(建议看一看,简单题,不复杂的)

题目链接

我的题解

这是我一开始写的版本:

//根据二叉树的前序遍历字符串,构建树,并输出中序遍历结果
#include <bits/stdc++.h>
using namespace std;

string s;
int len;

//定义二叉树的节点
struct node
{
char c;
node *left;
node *right;
};

void createTree(node *root)
{
if (len >= s.length())
return; //到最后一个字符了,结束返回(字符串最后3个字符一定是###)
char c = s[len++];
if (c == '#')
{
root = NULL;
return;
}
else
{
root = new node;
root->c = c;
//左右孩子初始化
root->left = NULL;
root->right = NULL;
//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(root->left);
createTree(root->right);
}
}

void midOrdTrave(node *node)
{
//中序遍历
if (node != NULL)
{
midOrdTrave(node->left);
cout << node->c << " ";
midOrdTrave(node->right);
}
return;
}

int main()
{
while (cin >> s)
{
node *binaryTreeRoot=NULL;
len = 0;
createTree(binaryTreeRoot);
midOrdTrave(binaryTreeRoot);
cout << endl;
}

return 0;
}

编译时报了Segment fault,提交时报了runtime error错误

不难知道是指针访问了不该访问的内存空间了。

后来发现,是因为main函数中,binaryTreeRoot在经历了createTree之后,仍然是NULL,这是为什么呢?

看过上面的内容,就明白了,错在createTree函数的传参。createTree函数的形参root在new node的时候便指向了一块新的内存地址了,而此时,实参binaryTreeRoot是没有任何变化的,仍然指向NULL。所以之后进行中序遍历,肯定会报错。

这个时候,我们在传参时,因为我们需要操作的是传入的实参指针而不是形参指针,就需要把binaryTreeRoot的这个指针的地址传进去(和经典的swap例子一样的),才能起到一个真正的、数据双向绑定的效果,即:

int main()
{
while (cin >> s)
{
node *binaryTreeRoot = NULL;
len = 0;
createTree(&binaryTreeRoot);

midOrdTrave(binaryTreeRoot);
cout << endl;
}

return 0;
}

这时候,createTree接收到的参数是地址的地址,也就相当于一个二重指针,所以在定义形参时,需要做一定修改,即

//建树
void createTree(node **root)

在该函数内使用时,**root为被指向的node,*root为指向节点的指针,root为指针的地址。

我们在修改节点内容时,便应该使用(*root->c=new_char)这样的方式来更新。但直接使用会报编译错误,所以我们使用一个tmp指针来过度。

//创建一个新节点
node *tmp = new node;
tmp->c = c;
tmp->left = NULL;
tmp->right = NULL;

*root = tmp;

//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(&(tmp->left));
createTree(&(tmp->right));

后两行递归也是一样的道理。

那么完整的更新后的代码如下(也可以看题解,一样的):

//根据二叉树的前序遍历字符串,构建树,并输出中序遍历结果
#include <bits/stdc++.h>
using namespace std;

string s;
int len; //每次遍历到的字符串中字符的index

//定义二叉树的节点
struct node
{
char c;
node *left;
node *right;
};

//建树
void createTree(node **root)
{
if (len >= s.length())
return; //到最后一个字符了,结束返回(字符串最后3个字符一定是###)
char c = s[len++];

if (c == '#')
{
*root = NULL;
return;
}
else
{
//创建一个新节点
node *tmp = new node;
tmp->c = c;
tmp->left = NULL;
tmp->right = NULL;

*root = tmp;

//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(&(tmp->left));
createTree(&(tmp->right));
}
}

void midOrdTrave(node *node)
{
//中序遍历
if (node != NULL)
{
midOrdTrave(node->left);
cout << node->c << " ";
midOrdTrave(node->right);
}
return;
}

int main()
{
while (cin >> s)
{
node *binaryTreeRoot = NULL;
len = 0;
createTree(&binaryTreeRoot);

midOrdTrave(binaryTreeRoot);
cout << endl;
}

return 0;
}

回顾一下两个重点

  • 当指针作为参数传入函数时,是值传递而非引用传递
  • 区别指针(或者说*)在定义时和使用时的区别,切勿混淆

理解这篇文档后,在以后的指针上面基本不会有太多困难了!

猜你喜欢

转载自blog.csdn.net/zhong233/article/details/126229075
今日推荐