C++ 指针运算符(& 和 *)(建议收藏)

C++ 指针运算符(& 和 *):从零开始理解内存操作

在学习 C++ 的过程中,指针是一个绕不开的核心概念。很多初学者在面对“C++ 指针运算符(& 和 *)”时感到困惑,甚至畏惧。其实,只要掌握其本质,指针就像一把精准的“钥匙”,能直接打开内存的“门锁”。本文将带你一步步揭开指针的神秘面纱,结合实际代码和生活类比,让你真正理解 & 和 * 的作用。


什么是地址?理解内存的基本单位

在计算机中,每一个变量都存储在内存中。我们可以把内存想象成一个巨大的“房间集合”,每个房间都有一个唯一的编号,这个编号就是“地址”。当你声明一个变量时,系统会为你分配一个房间(内存空间),并记录下这个房间的编号。

例如,下面这段代码:

int age = 25;

系统会在内存中为 age 分配一个空间,比如地址为 0x7fff5fbff6ac。这个地址就是变量的“身份证号”。而 C++ 中的 & 运算符,就是用来获取这个“身份证号”的工具。

int age = 25;
cout << "age 的内存地址是: " << &age << endl;

注释:&age 返回的是 age 变量在内存中的地址,类型为 int*(即“指向 int 的指针”)。


& 运算符:取地址,获取变量的“身份证号”

& 是 C++ 中的“取地址”运算符。它的作用是:获取一个变量在内存中的地址

我们来看一个完整的示例:

#include <iostream>
using namespace std;

int main() {
    int score = 98;
    
    // 使用 & 获取 score 的地址
    cout << "score 的值是: " << score << endl;
    cout << "score 的地址是: " << &score << endl;
    
    // 输出地址类型(方便理解)
    cout << "地址类型是: " << typeid(&score).name() << endl;
    
    return 0;
}

注释:

  • &score 返回的是 score 的内存地址,比如 0x7fff5fbff6a8
  • typeid(&score).name() 用于查看地址的类型,结果是 int*,表示“指向 int 类型的指针”。
  • 这个地址是固定的,只要程序运行期间不重新分配内存,它就不会变。

常见误区澄清

有些初学者会误以为 & 是“取值”的操作,其实恰恰相反——& 是“取地址”,而取值要用 *。这就像你去银行,身份证号是你的“地址”,而“取钱”才需要出示身份证。


* 运算符:解引用,通过地址“打开”变量

* 是 C++ 中的“解引用”运算符。它的作用是:通过一个地址,找到该地址所指向的变量,并获取它的值

我们继续上面的例子,加入指针变量:

#include <iostream>
using namespace std;

int main() {
    int score = 98;
    
    // 声明一个指针变量,用来存储地址
    int* ptr = &score;  // ptr 存储 score 的地址
    
    cout << "score 的值是: " << score << endl;
    cout << "ptr 存储的地址是: " << ptr << endl;
    cout << "通过 ptr 解引用得到的值是: " << *ptr << endl;
    
    // 修改值:通过指针修改原变量
    *ptr = 100;
    cout << "修改后 score 的值是: " << score << endl;
    
    return 0;
}

注释:

  • int* ptr = &score; 声明一个名为 ptr 的指针变量,它能存储 int 类型变量的地址。
  • *ptr 表示“从 ptr 中取出地址,然后访问该地址所指向的值”。
  • *ptr = 100; 意味着:把 ptr 所指向的内存位置(即 score)的值改为 100。
  • 因为 ptr 指向 score,所以 score 的值也被改变了。

形象比喻

把指针想象成一张“地图上的标记点”:

  • &score 就像是“标记点的位置编号”;
  • ptr 就是“这张地图上的标记点”;
  • *ptr 就是“顺着标记点,去到那个位置,看看那里的内容”。

指针的类型必须匹配:类型安全的重要性

C++ 是强类型语言,指针的类型必须与它所指向的数据类型一致。例如,int* 只能指向 int 类型的变量,不能随意指向 doublechar

#include <iostream>
using namespace std;

int main() {
    int age = 30;
    double salary = 5000.5;
    
    // 正确:int* 指向 int
    int* ptr1 = &age;
    cout << "int* 指向 int: " << *ptr1 << endl;
    
    // 错误示例(编译报错):
    // int* ptr2 = &salary;  // 不能把 double 的地址赋给 int* 指针
    // cout << *ptr2 << endl;
    
    // 正确做法:使用正确的指针类型
    double* ptr3 = &salary;
    cout << "double* 指向 double: " << *ptr3 << endl;
    
    return 0;
}

注释:

  • int* ptr1 = &age; 正确,类型匹配。
  • int* ptr2 = &salary; 会导致编译错误,因为 salarydouble 类型,不能赋给 int*
  • double* ptr3 = &salary; 正确,指针类型与数据类型一致。

提示:类型不匹配会导致未定义行为,可能造成程序崩溃或数据损坏,务必小心。


指针与数组:内存连续性的体现

数组在内存中是连续存储的。指针可以方便地遍历数组,这是 C++ 中高效操作数组的核心技巧。

#include <iostream>
using namespace std;

int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int size = 5;
    
    // 数组名本身就是一个地址(首元素的地址)
    int* ptr = numbers;  // 等价于 &numbers[0]
    
    cout << "数组首地址: " << numbers << endl;
    cout << "ptr 指向的地址: " << ptr << endl;
    
    // 使用指针遍历数组
    for (int i = 0; i < size; i++) {
        cout << "numbers[" << i << "] = " << *(ptr + i) << endl;
    }
    
    // 也可以用 ++ 指针移动
    ptr = numbers;  // 重置指针
    for (int i = 0; i < size; i++) {
        cout << "第 " << i + 1 << " 个元素: " << *ptr << endl;
        ptr++;
    }
    
    return 0;
}

注释:

  • numbers 本身就是一个指针,指向数组第一个元素的地址。
  • ptr + i 表示从当前地址往后移动 i 个 int 大小的位置。
  • *ptr 是解引用,获取当前指针指向的值。
  • ptr++ 会使指针移动到下一个元素的地址。

指针算术的原理

在内存中,ptr + 1 并不是“加 1”字节,而是“加一个 int 类型的大小”(通常是 4 字节)。这就是指针算术的“智能”之处。


实际应用:函数参数传递与动态内存管理

1. 函数中使用指针修改原变量

有时我们需要在函数中修改主函数中的变量,这时必须使用指针。

#include <iostream>
using namespace std;

// 函数:通过指针修改传入的变量
void increment(int* p) {
    *p = *p + 1;  // 解引用后加 1
}

int main() {
    int value = 10;
    
    cout << "修改前 value = " << value << endl;
    increment(&value);  // 传入地址
    cout << "修改后 value = " << value << endl;
    
    return 0;
}

注释:

  • increment(&value) 传入 value 的地址。
  • *p = *p + 1 表示:找到地址 p 所指向的变量,并将其值加 1。
  • 原变量 value 被成功修改。

2. 动态内存分配:new 和 delete

指针常用于动态分配内存,避免栈溢出。

#include <iostream>
using namespace std;

int main() {
    int* ptr = new int(42);  // 动态分配一个 int,初始化为 42
    
    cout << "动态分配的值: " << *ptr << endl;
    
    delete ptr;  // 释放内存,避免内存泄漏
    
    return 0;
}

注释:

  • new int(42) 在堆上分配内存,并初始化为 42。
  • ptr 是一个指针,指向这块内存。
  • delete ptr 释放内存,必须调用,否则会造成内存泄漏。

总结:掌握 C++ 指针运算符(& 和 *)的关键点

  • & 是“取地址”,用于获取变量的内存位置;
  • * 是“解引用”,用于通过地址访问变量的值;
  • 指针必须与目标类型匹配,否则编译失败;
  • 指针与数组结合使用,能高效遍历和操作数据;
  • 在函数中使用指针,可实现“传址调用”,修改原变量;
  • 动态内存管理依赖指针,但必须注意 delete 的使用。

掌握 C++ 指针运算符(& 和 *)不仅是编程技能的提升,更是理解底层运行机制的关键一步。它让你从“写代码”迈向“控制代码”,是迈向 C++ 高级编程的必经之路。多写、多调试、多思考,你会发现,指针并没有那么可怕,反而非常强大。

记住:你不是在操作数据,而是在操作“数据的位置”。一旦理解这一点,C++ 的世界将为你打开一扇新大门。