C++ 指针与引用

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

一、指针详解

1. 基本概念

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

int x = 10;
int* p = &x;  // p 存储 x 的地址

2. 指针操作

  • 声明指针type* pointerName

  • 取地址&variable

  • 解引用*pointer

  • 指针运算++--+-

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

3. 多级指针

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

int x = 10;
int* p = &x;
int** pp = &p;  // 指向指针的指针

4. 指针与数组

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

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

5. 指针的const限定

有三种const指针形式:

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. 基本概念

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

int x = 10;
int& r = x;  // r 是 x 的引用
r = 20;      // 等同于 x = 20

2. 引用特性

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

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

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

3. 引用作为函数参数

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

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

void swap(int a, int b) {  // 值传递
    int temp = a;
    a = b;
    b = temp;
}

问题在于

  • 当调用 swap(x, y) 时,函数会创建 a 和 b 的副本

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

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

2) 用引用时:

void swap(int& a, int& b) {  // 引用传递
    int temp = a;
    a = b;
    b = temp;
}

优势

  • a 和 b 是原始变量 x 和 y 的别名(不是副本)

  • 对 a 和 b 的操作直接作用于 x 和 y

  • 函数结束后,x 和 y 的值确实被交换了

3) 内存角度解释

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

4. const引用

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

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. 指针与引用的转换

int x = 10;
int* p = &x;
int& r = *p;  // 通过指针创建引用

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

2. 函数返回引用

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

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) 为什么需要返回引用

对比两种返回方式:

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

工作流程分析

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博客

int&& rref = 10;  // 右值引用

4. 指针与引用的性能

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

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

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

四、使用建议

  1. 优先使用引用

    • 函数参数传递

    • 操作符重载

    • 不希望参数为null的情况

  2. 必须使用指针

    • 需要处理动态内存

    • 需要重新指向不同对象

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

  3. 避免

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

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

    • 过度使用多级指针

五、综合示例

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++中引用使用更广泛,但指针在底层操作中不可替代

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值