C++ 入门学习经验 05——指针(一):从交换变量开始,搞懂值传递、指针和引用
大家好啊!这里是阳阳的博客,一个正在努力学习技术的大学生。
前面两篇我们聊了递归,也用几个例子看到了:有时候代码只是顺序换了一下,结果就完全不一样。
那今天这篇,咱们来聊另一个很多初学者都会遇到的问题:为什么我在函数里面明明改了变量,回到主函数里却一点变化都没有?
这个问题其实特别适合用来引出 C++ 里面几个很重要的概念:值传递、指针和引用。这几个东西刚开始看确实容易绕,但只要从“函数到底拿到的是什么”这个角度去理解,就会清楚很多。
好了,废话不多说,咱们直接开始。
简单案例:交换函数
我们先看一段代码。你觉得它会输出什么?
#include <iostream>
using namespace std;
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 5;
swap(x, y);
cout << x << " " << y << endl; // 期待输出 5 3 ?
return 0;
}
先暂停一下,自己想一想。
运行结果其实是:3 5 —— 完全没有交换。
怎么回事?明明 swap 函数里已经交换了a 和 b,为什么外面的 x 和 y 没变?
这就要引出初学者最容易踩的第一个坑:值传递。下面我们就开始讲讲要怎么理解这部分内容
值传递:函数里的变量是“复印件”
在 C++ 里,如果我们直接把变量传给函数,比如 swap(x, y),函数里面的 a 和 b 并不是原来的 x 和 y,而是它们的 副本。
可以这样理解:
你把一份资料复印给别人,别人把复印件改得再多,也不会影响你手里的原件。
所以刚才那段代码真正发生的过程大概是这样:
main里面有两个变量:x = 3,y = 5。调用
swap(x, y)时,把x和y的值复制了一份,分别给了a和b。函数里面交换的是
a和b这两个副本。函数结束后,
a和b被销毁。原来的
x和y从头到尾都没有被改过。
所以这就是我们常说的:函数里改了,外面却没变。
那如果我就是想让函数真的改到外面的变量,该怎么办呢?很自然的想法就是:不要只给函数一份复印件,而是告诉它原变量在哪里。
这就引出了指针。
指针:保存地址的变量
指针这个词刚听起来可能有点抽象,但先不用想得太复杂。咱们可以先记住一句话:
指针就是用来保存地址的变量。
比如:
int x = 10;
int* p = &x;
这里有两个符号需要注意:
&x表示取出变量x的地址。
int* p表示p是一个指针变量,它里面存的是一个int类型变量的地址。
那既然 p 里面存着 x 的地址,我们就可以通过 p 找到 x,这一步叫 解引用:
cout << *p << endl; // 输出 10
这里的 *p 就可以理解成:顺着 p 里面保存的地址,找到那个变量的值。
所以如果要真正交换两个变量,就可以把变量的地址传进函数:
void swapByPointer(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 3, y = 5;
swapByPointer(&x, &y);
cout << x << " " << y << endl;
return 0;
}
这次输出就是:
5 3
因为函数拿到的不再是 x 和 y 的复印件,而是它们的地址。函数通过地址找到了原变量,所以修改就真的生效了。
不过大家可能也发现了,指针写起来会多一些 * 和 &,刚开始很容易看晕。那有没有一种写法,既能改到原变量,又看起来更像普通变量呢?
有,这就是引用。
引用:给变量起一个“别名”
引用可以简单理解成:给一个变量起了另一个名字。
比如:
int x = 10;
int& ref = x;
ref = 20;
cout << x << endl; // 输出 20
这里 ref 是 x 的引用,也就是 x 的别名。我们修改 ref,其实就是在改 x。
所以交换两个变量时,也可以这样写:
void swapByReference(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 5;
swapByReference(x, y);
cout << x << " " << y << endl;
return 0;
}
这次同样会输出:
5 3
引用的好处就是调用的时候很自然,还是直接写 swapByReference(x, y),但是函数内部拿到的不是副本,而是原变量的别名。
所以我觉得初学的时候可以这样记:
值传递看成复印件,指针 = 地址,引用 = 别名。
三种方式简单对比
为了方便理解,咱们放在一起看一下:
| 方式 | 函数参数写法 | 调用时写法 | 能不能改外面的变量 |
|---|---|---|---|
| 值传递 | int a |
func(x) |
不能 |
| 指针传递 | int* a |
func(&x) |
能 |
| 引用传递 | int& a |
func(x) |
能 |
这里最容易混的地方就是两个 &:
在
int& ref = x;里面,&表示引用。在
&x里面,&表示取地址。
它们长得一样,但出现在不同位置时,意思是不一样的。这个刚开始不用死记硬背,多看几段代码就会慢慢熟悉。
指针和引用还有一个重要区别
引用和指针都能让函数改到外面的变量,但它们并不完全一样。
引用一般定义时就要绑定到一个变量,并且后面不能再改成引用别的变量:
int x = 10, y = 20;
int& ref = x;
ref = y; // 这不是让 ref 改绑到 y,而是把 y 的值赋给 x
而指针就更灵活一点,它可以先指向 x,后面再指向 y,也可以指向空:
int x = 10, y = 20;
int* p = &x;
p = &y;
p = nullptr;
所以简单来说:
引用更像固定的别名,用起来简洁。
指针更像可以改变方向的地址变量,更灵活,但也更需要小心。
至于什么时候用指针,什么时候用引用,这个后面结合数组、函数返回值、动态内存的时候会更好理解。今天先把最基础的概念弄清楚就行。
写在最后
今天咱们从一个简单交换函数的例子入手,搞清楚了:
值传递:类比复印件,不改原件。
指针传递:传地址,能改原件,但写法稍复杂。
引用传递:别名,能改原件,写法简洁。
所以,只是参数的形式换一下——加个 & 或 *,结果就不一样了。
后面我们会继续聊 数组与指针的关系、动态内存分配 等等。如果你对指针还觉得晕,没关系,多练习写几遍自然就熟了。
如果这篇文章对你有帮助,麻烦点赞、关注和收藏吧,谢谢! 😄
有什么问题或者想法,欢迎在评论区留言,我们一起交流!
我们下篇见~
更多推荐


所有评论(0)