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 会影响实参 给原件起了个绰号

学习建议:

如果你觉得指针太绕,可以先记住这个黄金法则

  1. 如果只是想读数据,用 const 引用(效率高且安全)。
  2. 如果想修改函数外的变量,优先用 引用
  3. 除非你需要处理 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;
}


运行结果深度解析

当你运行这段程序时,你会看到类似下面的输出(具体的十六进制地址每次运行都会变):

  1. 值传递 (byValue)
  • 你会发现它的地址和 main 函数里 a 的地址完全不同
  • 结论:系统在内存里新开了一个房间,把 100 拷了进去。你在函数里改这个 x,原来的 a 稳如泰山。
  1. 指针传递 (byPointer)
  • 打印出来的地址和 a 的地址一模一样
  • 结论:函数拿到了 a 房间的钥匙(地址),它可以直接开门进去改掉 a
  1. 引用传递 (byReference)
  • 打印出来的地址也和 a 一模一样
  • 结论r 仅仅是 a 的另一个名字。就像你叫“张三”,外号叫“小张”,地址还是那一个。

💡 一个帮你避坑的小技巧

很多初学者分不清 & 到底是在取地址还是在声明引用。看这个:

  • 如果左边有类型(如 int& r = a;),它是引用(起外号)。
  • 如果左边没类型(如 p = &a;),它是取地址(拿钥匙)。

同样:

  • 如果左边有类型(如 int* p;),它是指针声明(造钥匙盒)。
  • 如果左边没类型(如 *p = 20;),它是解引用(用钥匙开门)。
posted @ 2026-02-24 11:59  长松入霄汉远望不盈尺  阅读(1)  评论(0)    收藏  举报