Skip to content

📍 05. 指针入门

指针 是 C/C++ 中最重要、也最独特的概念。如果说 Python 和 Java 把内存管理"藏"起来了,那么 C++ 则把内存的钥匙直接交到了你手中。

指针让你能直接操作计算机的内存——这是一把强大的武器,但也需要小心使用。

一、内存地址 🏠

计算机的内存就像一栋巨大的公寓楼,每个房间都有一个唯一的门牌号。

cpp
int a = 10;

当你定义这个变量时,计算机会在内存中找一个"房间"(4 字节)来存放 10,这个房间的"门牌号"就是内存地址

我们可以用 & 运算符来获取变量的地址:

cpp
#include <iostream>
using namespace std;

int main() {
    int a = 10;

    cout << "变量 a 的值:" << a << endl;
    cout << "变量 a 的地址:" << &a << endl;

    return 0;
}

输出(每次运行地址可能不同):

变量 a 的值:10
变量 a 的地址:0x61fe1c

NOTE

0x61fe1c十六进制表示的内存地址。0x 开头的数字就是十六进制。 每次运行程序时,操作系统分配的内存地址可能不一样。

二、什么是指针?📍

指针 就是一个变量,但它存储的不是普通的值,而是另一个变量的内存地址

普通变量存的是"数据",指针变量存的是"数据存放在哪里"。

1. 指针的声明

cpp
int* p;   // p 是一个指针,指向 int 类型的数据
char* c;  // c 是一个指针,指向 char 类型的数据
double* d; // d 是一个指针,指向 double 类型的数据

* 表示这是一个指针类型。int* 读作"指向 int 的指针"。

2. 取地址和取值

两个最重要的指针运算符:

运算符名称作用
&取地址符获取变量的内存地址
*解引用符通过地址访问该地址上存储的值

3. 基本使用

cpp
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int* p = &a;  // p 存储了 a 的地址

    cout << "a 的值:" << a << endl;       // 10
    cout << "p 的值(a的地址):" << p << endl;  // 0x61fe1c
    cout << "p 指向的值:" << *p << endl;   // 10(解引用)

    *p = 20;  // 通过指针修改 a 的值
    cout << "修改后 a 的值:" << a << endl;  // 20

    return 0;
}

图解:

 变量 a             指针 p
+-------+         +---------+
|  20   |  <------|  &a的地址 |
+-------+         +---------+
  地址:0x100        地址:0x200
  
  *p = 20(通过 p 找到 a 的地址,读取/修改那里的值)

TIP

可以把 int* p = &a; 理解为:"p 是一个箭头,箭头指向了 a 所在的位置"。

  • p 是箭头本身(存储地址)
  • *p 是箭头指向的地方(访问值)

三、指针的常见用途 🛠️

1. 通过函数修改外部变量

还记得吗?我们之前学的函数参数是值传递——函数内部修改形参不会影响实参。

cpp
void swapBad(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
    // 这里交换的是副本,main 里的 a,b 不受影响
}

int main() {
    int a = 3, b = 5;
    swapBad(a, b);
    cout << a << " " << b;  // 还是 3 5,没交换成功!
    return 0;
}

用指针就能解决——把地址传进去,直接修改地址上的值:

cpp
#include <iostream>
using namespace std;

void swapGood(int* x, int* y) {
    int temp = *x;  // 取 x 指向的值
    *x = *y;        // 把 y 指向的值赋给 x 指向的位置
    *y = temp;      // 把 temp 赋给 y 指向的位置
}

int main() {
    int a = 3, b = 5;

    cout << "交换前:a = " << a << ", b = " << b << endl;

    swapGood(&a, &b);  // 传入 a 和 b 的地址

    cout << "交换后:a = " << a << ", b = " << b << endl;

    return 0;
}

输出:

交换前:a = 3, b = 5
交换后:a = 5, b = 3

NOTE

这是 C++ 中"指针做函数参数"的核心用途——让函数能修改外部的变量。 实际上 C++ 还有更安全的引用(Reference) 方式来实现同样的效果,后面会介绍。

2. 指针和数组

数组名本质上就是一个指针常量——它指向数组的第一个元素。

cpp
#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    cout << "数组名 arr 的值:" << arr << endl;    // 地址
    cout << "arr[0] 的地址:" << &arr[0] << endl;  // 和 arr 相同
    cout << "arr[0] 的值:" << *arr << endl;       // 10
    cout << "arr[1] 的值:" << *(arr + 1) << endl;  // 20

    // 用指针遍历数组
    int* p = arr;
    for (int i = 0; i < 5; i++) {
        cout << "arr[" << i << "] = " << *(p + i) << endl;
    }

    return 0;
}

关键理解:

写法含义
arr数组首地址(等价于 &arr[0]
*arr第一个元素的值(等价于 arr[0]
arr + i第 i 个元素的地址(等价于 &arr[i]
*(arr + i)第 i 个元素的值(等价于 arr[i]

3. 动态内存分配

有时候我们不知道需要多大的数组,想在程序运行时才决定大小:

cpp
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入数组大小:";
    cin >> n;

    // 在堆上动态分配 n 个 int 的空间
    int* arr = new int[n];

    // 像普通数组一样使用
    for (int i = 0; i < n; i++) {
        arr[i] = i * 10;
    }

    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 使用完毕后,必须手动释放内存!
    delete[] arr;

    return 0;
}

WARNING

new 分配的内存必须用 delete 释放!

  • newdelete(单个对象)
  • new[]delete[](数组)

忘记释放会导致内存泄漏——程序占用的内存越来越多,最终可能耗尽系统资源。

四、引用(Reference)🔗

引用是 C++ 对指针的一种"安全封装"。可以把引用理解为变量的别名

1. 基本使用

cpp
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int& ref = a;  // ref 是 a 的引用(别名)

    cout << "a = " << a << endl;    // 10
    cout << "ref = " << ref << endl; // 10

    ref = 20;  // 修改 ref 就是修改 a
    cout << "a = " << a << endl;    // 20

    return 0;
}

2. 用引用实现交换函数

cpp
void swapByReference(int& x, int& y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 3, b = 5;
    swapByReference(a, b);  // 直接传变量,不用 & 取地址
    cout << a << " " << b;  // 5 3 ✅
    return 0;
}

3. 指针 vs 引用

对比项指针引用
语法int* p = &a; *pint& r = a; r
可否为空可以(nullptr)不能为空,必须初始化
可否改变指向可以不能改变,始终引用同一个变量
安全性较低(空指针、野指针风险)较高
适用场景动态内存、数组操作、可选参数函数参数传递、操作符重载

TIP

能用引用的时候优先用引用,它比指针更安全、语法更简洁。 只有在需要"可以为空"或"动态内存管理"时才用指针。

五、空指针和 nullptr 🚫

指针如果没有任何指向,应该设置为 nullptr

cpp
int* p = nullptr;  // p 不指向任何地方

if (p != nullptr) {
    cout << *p << endl;  // 安全:确认 p 非空才使用
} else {
    cout << "指针为空" << endl;
}

DANGER

解引用空指针会导致程序崩溃!

cpp
int* p = nullptr;
cout << *p << endl;  // ❌ 程序会崩溃!

使用指针前务必检查是否为空

六、小结 🎯

  • 指针 是存储内存地址的变量,用 * 声明和访问。
  • & 取变量地址,* 解引用(访问指针指向的值)。
  • 指针可以用来:修改函数外部的变量、操作数组、动态内存分配。
  • 引用 是变量的别名,比指针更安全,能用引用时优先用引用。
  • new 分配的内存必须用 delete 释放,否则造成内存泄漏。
  • 使用指针前检查是否为空(nullptr)。

七、练习 📝

  1. 基本练习:定义一个 int 变量 x 和一个指向它的指针 p。通过 p 修改 x 的值并输出。

  2. 指针交换:用指针实现一个函数 void swap(int* a, int* b),交换两个整数的值。

  3. 指针遍历数组:定义一个数组 int arr[5] = {1, 2, 3, 4, 5},用指针遍历并输出所有元素。

  4. 字符串长度:用指针实现一个函数 int myStrlen(const char* str),计算 C 风格字符串的长度(不使用 strlen 库函数)。

  5. 引用实现:用引用而不是指针重写上面的交换函数。

  6. 动态数组:用 new 创建一个动态数组,输入 n 个学生的成绩,计算平均分,最后用 delete[] 释放内存。

  7. 挑战题:指针排序:用指针(而不是数组下标)实现冒泡排序。函数原型:void bubbleSort(int* arr, int n)

💬 与我联系 QQ:774165314 | 微信:ArinRiver