C++ 指针与引用

指针和引用是 C++ 中两个强大的特性,它们都提供了间接访问数据的能力,但在语法和语义上有重要区别。

一、指针详解

1. 基本概念

指针是一个变量,其值是另一个变量的内存地址。

cpp 复制代码
int x = 10;
int* p = &x;  // p 存储 x 的地址

2. 指针操作

  • 声明指针type* pointerName

  • 取地址&variable

  • 解引用*pointer

  • 指针运算++, --, +, -

cpp 复制代码
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;  // 指向数组首元素
cout << *(p + 2);  // 输出3,等同于 arr[2]

3. 多级指针

指针可以指向另一个指针:

cpp 复制代码
int x = 10;
int* p = &x;
int** pp = &p;  // 指向指针的指针

4. 指针与数组

数组名在大多数情况下会退化为指针:

cpp 复制代码
int arr[3] = {1, 2, 3};
int* p = arr;  // 合法
cout << p[1];  // 输出2

5. 指针的const限定

有三种const指针形式:

cpp 复制代码
int x = 10, y = 20;
const int* p1 = &x;  // 指向常量的指针(值不能改)
p1 = &y;  // 合法
// *p1 = 30;  // 非法

int* const p2 = &x;  // 常量指针(指向不能改)
*p2 = 30;  // 合法
// p2 = &y;  // 非法

const int* const p3 = &x;  // 指向常量的常量指针

二、引用详解

1. 基本概念

引用是变量的别名,必须在初始化时绑定到一个变量。

cpp 复制代码
int x = 10;
int& r = x;  // r 是 x 的引用
r = 20;      // 等同于 x = 20

2. 引用特性

  • 必须初始化:不能先声明后赋值

  • 不可重新绑定:一旦绑定就不能改变

  • 无独立内存:编译器通常将引用实现为常量指针

3. 引用作为函数参数

最常见的用途是函数参数传递:

cpp 复制代码
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(x, y);  // 直接传递变量
}

为什么不能直接用 int a

1) 值传递的问题(直接 int a

cpp 复制代码
void swap(int a, int b) {  // 值传递
    int temp = a;
    a = b;
    b = temp;
}

问题在于

  • 当调用 swap(x, y) 时,函数会创建 ab副本

  • 你只是在交换函数内部的副本,原变量 xy 的值不会改变

  • 函数结束后,副本被销毁,原值保持不变

2) 用引用时:

cpp 复制代码
void swap(int& a, int& b) {  // 引用传递
    int temp = a;
    a = b;
    b = temp;
}

优势

  • ab 是原始变量 xy别名(不是副本)

  • ab 的操作直接作用于 xy

  • 函数结束后,xy 的值确实被交换了

3) 内存角度解释

值传递 (int a) 引用传递 (int& a)
内存使用 创建新变量,复制值 不创建新变量,直接使用原变量
对原变量的影响 不影响原变量 直接影响原变量
函数调用后结果 xy 不变 xy 值交换

4. const引用

可以绑定到临时对象或不同类型:

cpp 复制代码
const int& r1 = 10;  // 合法
double d = 3.14;
const int& r2 = d;   // 合法,创建临时int对象

5. 引用与指针的区别

操作 指针 引用
声明 int* p = &x; int& r = x;
空值 可以 p = nullptr 必须绑定有效对象
重新绑定 可以改变指向 一旦绑定不能改变
访问 需要 *p 解引用 直接使用 r
地址操作 可以 &p 获取指针地址 &r 得到原变量地址
数组 可以指针运算访问数组 不能用于数组访问
多级间接 支持多级指针 不支持引用的引用

三、高级主题

1. 指针与引用的转换

cpp 复制代码
int x = 10;
int* p = &x;
int& r = *p;  // 通过指针创建引用

int& r2 = x;
int* p2 = &r2;  // 通过引用获取指针

2. 函数返回引用

可以返回引用实现链式调用:

cpp 复制代码
class MyArray {
    int data[10];  // 私有成员,存储10个整数的数组
public:
    // 重载下标运算符[]
    int& operator[](size_t index) {
        return data[index];  // 返回内部数组元素的引用
    }
};

MyArray arr;
arr[3] = 42;  // 返回引用可直接赋值

关键概念解析

1) 运算符重载 (operator[])

  • 允许我们自定义类对象使用 [] 运算符时的行为

  • 当写 arr[3] 时,实际上调用的是 arr.operator[](3)

2) 返回引用 (int&)

  • 返回类型是 int&(整数的引用)而不是 int(整数值)

  • 这使得我们可以修改返回的元素

3) 为什么需要返回引用

对比两种返回方式:

返回类型 示例 效果
int int operator[] 返回副本,无法修改原数组元素
int& int& operator[] 返回实际元素的引用,可以修改原值

工作流程分析

cpp 复制代码
MyArray arr;       // 创建MyArray对象
arr[3] = 42;       // 使用重载的下标运算符
  1. arr[3] 被解析为 arr.operator[](3)

  2. operator[] 方法返回 data[3] 的引用

  3. = 42 操作通过这个引用直接修改 data[3] 的值

3. 右值引用 (C++11)

右值引用 (C++11)-CSDN博客

cpp 复制代码
int&& rref = 10;  // 右值引用

4. 指针与引用的性能

在底层实现上,引用通常通过指针实现,但编译器可以优化:

  • 引用通常没有运行时开销

  • 指针可能因为间接寻址有轻微性能损失

四、使用建议

  1. 优先使用引用

    • 函数参数传递

    • 操作符重载

    • 不希望参数为null的情况

  2. 必须使用指针

    • 需要处理动态内存

    • 需要重新指向不同对象

    • 需要表示可选参数(可能为null)

  3. 避免

    • 返回局部变量的引用或指针

    • 指针和引用混用导致代码混乱

    • 过度使用多级指针

五、综合示例

cpp 复制代码
void process(int* ptr, int& ref) {
    *ptr *= 2;  // 通过指针修改
    ref += 5;   // 通过引用修改
}

int main() {
    int a = 10, b = 20;
    process(&a, b);
    
    cout << "a: " << a << endl;  // 输出20
    cout << "b: " << b << endl;  // 输出25
    
    int& r = a;
    int* p = &b;
    
    r++;        // a变为21
    (*p)++;     // b变为26
    
    cout << "a: " << a << endl;  // 输出21
    cout << "b: " << b << endl;  // 输出26
    
    return 0;
}

理解指针和引用的关键在于:

  • 指针是显式的内存地址操作

  • 引用是隐式的别名机制

  • 两者都可以修改原值,但抽象层次不同

  • 现代C++中引用使用更广泛,但指针在底层操作中不可替代

相关推荐
会唱歌的小黄李21 分钟前
【算法】贪心算法:最大数C++
c++·算法·贪心算法
NuyoahC23 分钟前
笔试——Day8
c++·算法·笔试
Pi_Qiu_40 分钟前
Python初学者笔记第十三期 -- (常用内置函数)
java·笔记·python
hsx6661 小时前
Android 基础筑基(一)
java
hy.z_7771 小时前
【数据结构】反射、枚举 和 lambda表达式
android·java·数据结构
墨染点香1 小时前
LeetCode Hot100 【1.两数之和、2.两数相加、3.无重复字符的最长子串】
算法·leetcode·职场和发展
從南走到北1 小时前
JAVA青企码协会模式系统源码支持微信公众号+微信小程序+H5+APP
java·微信·微信小程序·小程序·uni-app·微信公众平台
草履虫建模1 小时前
Ajax原理、用法与经典代码实例
java·前端·javascript·ajax·intellij-idea
强哥叨逼叨1 小时前
别被假象迷惑!揭秘 Java 线程池中“线程空着但任务卡着”的真相
java
秋说1 小时前
【PTA数据结构 | C语言版】二叉树层序序列化
c语言·数据结构·算法