C++的指针 、& 、* 、值传递、地址传递、引用传递
欢迎来到 C++ 的世界!别担心,你现在的“似懂非懂”其实是每个 C++ 学习者的必经之路。指针和引用就像是 C++ 的“入场券”,一旦理顺了,后面的路就好走多了。
我们可以把内存想象成一排带编号的储物柜:
1. 核心概念:* 与 & 的多重身份
这两个符号最让人头疼的地方在于,它们在声明变量和实际执行时的含义是不一样的。
* (星号)
- 在类型后(声明时): 表示“这是一个指针”。例如
int* p;意思是一个存放int类型地址的柜子钥匙。 - 在变量前(执行时): 表示“解引用”,即“顺着地址去找值”。例如
*p = 10;意思是把 10 放到钥匙指向的那个柜子里。
& (取地址/引用)
- 在变量前(执行时): 表示“取地址”。例如
&a意思是拿走变量a所在柜子的编号。 - 在类型后(声明时): 表示“引用”。这相当于给原有的柜子起了一个别名(绰号)。
2. 三种传递方式的直观对比
假设我们要写一个函数,让一个变量的值增加 10。
A. 值传递 (Pass by Value)
这是最简单的,相当于复印件。
void add(int a) { a += 10; }
- 原理: 函数创建了一个全新的副本。你在函数里怎么折腾副本,原本的变量都不会变。
- 缺点: 如果数据很大(比如一张巨大的图片),复印会非常慢。
B. 指址传递 (Pass by Pointer)
这相当于把柜子的钥匙交给函数。
void add(int* p) { *p += 10; }
- 原理: 你把变量的地址传进去,函数通过
*p找到原件并修改。 - 注意: 传入时需要用
add(&x)。
C. 引用传递 (Pass by Reference)
这相当于给柜子起个别名。
void add(int& r) { r += 10; }
- 原理:
r就是原变量本身,只是换了个名字。在函数里改r,外面也跟着变。 - 优势: 语法最简洁(像值传递一样写代码),效率最高(不用复制,也不用处理指针的繁琐)。
3. 总结对照表
| 方式 | 语法示例 | 效果 | 就像是... |
|---|---|---|---|
| 值传递 | void func(int x) |
修改 x 不影响实参 |
寄了一份文档副本给对方 |
| 指针传递 | void func(int* p) |
修改 *p 会影响实参 |
给了对方一张储物柜钥匙 |
| 引用传递 | void func(int& r) |
修改 r 会影响实参 |
给原件起了个绰号 |
学习建议:
如果你觉得指针太绕,可以先记住这个黄金法则:
- 如果只是想读数据,用
const 引用(效率高且安全)。 - 如果想修改函数外的变量,优先用
引用。 - 除非你需要处理
NULL(空地址)或者进行复杂的内存操作,否则尽量少用原始指针。
你想试着写一个小程序,观察一下这三种方式下变量地址的变化吗?我可以带你写一个实验代码。
太棒了,实践是消除“似懂非懂”的最好药方。
我们可以编写一个简单的“地址侦探”程序。通过打印变量在不同传递方式下的内存地址,你能亲眼看到谁是在“复印”,谁是在“操作本体”。
实验代码:地址侦探
你可以直接把这段代码复制到你的 IDE(如 Dev-C++、VS Code 或 Visual Studio)中运行:
#include <iostream>
using namespace std;
// 1. 值传递:传入的是副本
void byValue(int x) {
cout << "【值传递】 函数内变量地址: " << &x << ",值: " << x << endl;
}
// 2. 指针传递:传入的是地址(钥匙)
void byPointer(int* p) {
cout << "【指针传递】函数内指针指向的地址: " << p << ",值: " << *p << endl;
}
// 3. 引用传递:传入的是别名(本体)
void byReference(int& r) {
cout << "【引用传递】函数内引用关联的地址: " << &r << ",值: " << r << endl;
}
int main() {
int a = 100;
cout << "--- 原始变量 a 的地址: " << &a << " ---" << endl << endl;
byValue(a); // 传入 a 的值
byPointer(&a); // 传入 a 的地址
byReference(a); // 传入 a 本身(引用)
return 0;
}
运行结果深度解析
当你运行这段程序时,你会看到类似下面的输出(具体的十六进制地址每次运行都会变):
- 值传递 (
byValue):
- 你会发现它的地址和
main函数里a的地址完全不同。 - 结论:系统在内存里新开了一个房间,把
100拷了进去。你在函数里改这个x,原来的a稳如泰山。
- 指针传递 (
byPointer):
- 打印出来的地址和
a的地址一模一样。 - 结论:函数拿到了
a房间的钥匙(地址),它可以直接开门进去改掉a。
- 引用传递 (
byReference):
- 打印出来的地址也和
a一模一样。 - 结论:
r仅仅是a的另一个名字。就像你叫“张三”,外号叫“小张”,地址还是那一个。
💡 一个帮你避坑的小技巧
很多初学者分不清 & 到底是在取地址还是在声明引用。看这个:
- 如果左边有类型(如
int& r = a;),它是引用(起外号)。 - 如果左边没类型(如
p = &a;),它是取地址(拿钥匙)。
同样:
- 如果左边有类型(如
int* p;),它是指针声明(造钥匙盒)。 - 如果左边没类型(如
*p = 20;),它是解引用(用钥匙开门)。

浙公网安备 33010602011771号