Skip to content

指针

指针是C++最重要也是最具特色的概念之一。指针允许直接操作内存地址,是C++强大功能的核心。本章将详细介绍指针的概念和使用方法。

指针基础

指针的定义和使用

cpp
#include <iostream>
using namespace std;

int main() {
    // ============ 指针基础 ============
    
    int num = 10;
    
    // & 取地址运算符:获取变量的内存地址
    cout << "num的值: " << num << endl;
    cout << "num的地址: " << &num << endl;
    
    // 定义指针:类型* 指针名
    int* ptr;        // 定义一个int类型的指针
    ptr = &num;      // ptr指向num
    
    // 定义并初始化
    int* ptr2 = &num;
    
    // * 解引用运算符:获取指针指向的值
    cout << "\nptr指向的地址: " << ptr << endl;
    cout << "ptr指向的值: " << *ptr << endl;
    
    // 通过指针修改值
    *ptr = 20;
    cout << "修改后num的值: " << num << endl;
    
    
    // ============ 指针的类型 ============
    
    int i = 10;
    double d = 3.14;
    char c = 'A';
    
    int* intPtr = &i;      // int指针
    double* doublePtr = &d;  // double指针
    char* charPtr = &c;    // char指针
    
    cout << "\n各类型指针:" << endl;
    cout << "int指针: " << intPtr << ", 值: " << *intPtr << endl;
    cout << "double指针: " << doublePtr << ", 值: " << *doublePtr << endl;
    cout << "char指针: " << (void*)charPtr << ", 值: " << *charPtr << endl;
    
    
    // ============ 空指针 ============
    
    // C风格空指针
    int* nullPtr1 = NULL;
    
    // C++风格空指针(推荐)
    int* nullPtr2 = nullptr;
    
    // 检查空指针
    if (nullPtr2 == nullptr) {
        cout << "\n这是一个空指针" << endl;
    }
    
    // 解引用空指针是危险的!
    // *nullPtr2 = 10;  // 错误:段错误
    
    return 0;
}

指针运算

cpp
#include <iostream>
using namespace std;

int main() {
    // ============ 指针算术运算 ============
    
    int arr[] = {10, 20, 30, 40, 50};
    int* ptr = arr;  // 数组名就是首元素的地址
    
    cout << "===== 指针算术运算 =====" << endl;
    
    // 指针加整数
    cout << "*ptr = " << *ptr << endl;           // 10
    cout << "*(ptr + 1) = " << *(ptr + 1) << endl;  // 20
    cout << "*(ptr + 2) = " << *(ptr + 2) << endl;  // 30
    
    // 指针自增
    ptr++;
    cout << "\nptr++后: *ptr = " << *ptr << endl;  // 20
    
    ptr++;
    cout << "ptr++后: *ptr = " << *ptr << endl;  // 30
    
    // 指针减整数
    cout << "*(ptr - 1) = " << *(ptr - 1) << endl;  // 20
    
    // 指针相减(得到元素个数)
    int* start = arr;
    int* end = arr + 5;
    cout << "\n指针相减: " << end - start << endl;  // 5
    
    // 指针比较
    cout << "start < end: " << (start < end) << endl;  // true
    
    
    // ============ 指针与数组 ============
    
    cout << "\n===== 指针与数组 =====" << endl;
    
    int numbers[] = {1, 2, 3, 4, 5};
    int* p = numbers;
    
    // 两种等价的访问方式
    for (int i = 0; i < 5; i++) {
        cout << "numbers[" << i << "] = " << numbers[i];
        cout << ", *(p + " << i << ") = " << *(p + i) << endl;
    }
    
    // 使用指针遍历
    cout << "\n使用指针遍历: ";
    for (int* q = numbers; q < numbers + 5; q++) {
        cout << *q << " ";
    }
    cout << endl;
    
    
    // ============ 不同类型的指针步长 ============
    
    cout << "\n===== 指针步长 =====" << endl;
    
    int intArr[5] = {1, 2, 3, 4, 5};
    double doubleArr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
    
    int* ip = intArr;
    double* dp = doubleArr;
    
    cout << "int指针步长:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "地址: " << (ip + i) << ", 值: " << *(ip + i) << endl;
    }
    
    cout << "\ndouble指针步长:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "地址: " << (dp + i) << ", 值: " << *(dp + i) << endl;
    }
    
    return 0;
}

指针与const

cpp
#include <iostream>
using namespace std;

int main() {
    // ============ const与指针的组合 ============
    
    int value = 10;
    int another = 20;
    
    // 1. 指向常量的指针(底层const)
    // 不能通过指针修改值,但可以改变指针指向
    const int* ptr1 = &value;
    
    cout << "指向常量的指针:" << endl;
    cout << "*ptr1 = " << *ptr1 << endl;
    
    // *ptr1 = 30;  // 错误:不能修改指向的值
    ptr1 = &another;  // 正确:可以改变指向
    cout << "改变指向后 *ptr1 = " << *ptr1 << endl;
    
    // 2. 常量指针(顶层const)
    // 不能改变指针指向,但可以修改值
    int* const ptr2 = &value;
    
    cout << "\n常量指针:" << endl;
    cout << "*ptr2 = " << *ptr2 << endl;
    
    *ptr2 = 30;  // 正确:可以修改值
    cout << "修改值后 *ptr2 = " << *ptr2 << endl;
    // ptr2 = &another;  // 错误:不能改变指向
    
    // 3. 指向常量的常量指针
    // 既不能修改值,也不能改变指向
    const int* const ptr3 = &value;
    
    cout << "\n指向常量的常量指针:" << endl;
    cout << "*ptr3 = " << *ptr3 << endl;
    // *ptr3 = 40;  // 错误:不能修改值
    // ptr3 = &another;  // 错误:不能改变指向
    
    
    // ============ 记忆技巧 ============
    
    /*
     * 记忆技巧:从右往左读
     * 
     * const int* p     -> p is a pointer to const int
     *                    (p是指向常量int的指针)
     * 
     * int* const p     -> p is a const pointer to int
     *                    (p是指向int的常量指针)
     * 
     * const int* const p -> p is a const pointer to const int
     *                      (p是指向常量int的常量指针)
     */
    
    return 0;
}

引用

cpp
#include <iostream>
using namespace std;

int main() {
    // ============ 引用基础 ============
    
    int num = 10;
    
    // 引用:变量的别名
    // 定义时必须初始化,之后不能改变引用的对象
    int& ref = num;  // ref是num的引用
    
    cout << "num = " << num << endl;
    cout << "ref = " << ref << endl;
    cout << "&num = " << &num << endl;
    cout << "&ref = " << &ref << endl;  // 地址相同
    
    // 通过引用修改值
    ref = 20;
    cout << "\n修改ref后 num = " << num << endl;
    
    // 通过变量修改值
    num = 30;
    cout << "修改num后 ref = " << ref << endl;
    
    
    // ============ 引用与指针的区别 ============
    
    cout << "\n===== 引用与指针的区别 =====" << endl;
    
    int a = 10, b = 20;
    
    // 指针
    int* ptr = &a;
    cout << "指针指向a: *ptr = " << *ptr << endl;
    ptr = &b;  // 可以改变指向
    cout << "指针指向b: *ptr = " << *ptr << endl;
    
    // 引用
    int& ref2 = a;
    cout << "引用绑定a: ref2 = " << ref2 << endl;
    ref2 = b;  // 这不是改变引用,而是赋值!
    cout << "赋值b: a = " << a << endl;  // a变成了20
    
    
    // ============ 引用的特点 ============
    
    // 1. 必须初始化
    // int& r;  // 错误:引用必须初始化
    
    // 2. 一旦绑定不能改变
    int x = 10, y = 20;
    int& r = x;
    r = y;  // 这是赋值,不是改变引用
    cout << "\nx = " << x << endl;  // x = 20
    
    // 3. 没有空引用
    // int& nullRef = nullptr;  // 错误
    
    // 4. 没有引用的引用
    // int&& refRef = r;  // 这是右值引用,不是引用的引用
    
    
    // ============ 常量引用 ============
    
    const int& constRef = 100;  // 可以绑定到字面量
    cout << "\nconstRef = " << constRef << endl;
    // constRef = 200;  // 错误:不能修改
    
    // 常量引用可以绑定到不同类型(会创建临时变量)
    const double& dRef = 10;  // int -> double
    cout << "dRef = " << dRef << endl;
    
    return 0;
}

智能指针

cpp
#include <iostream>
#include <memory>  // 智能指针头文件
using namespace std;

// 测试类
class Resource {
public:
    string name;
    Resource(string n) : name(n) {
        cout << "创建资源: " << name << endl;
    }
    ~Resource() {
        cout << "销毁资源: " << name << endl;
    }
    void use() {
        cout << "使用资源: " << name << endl;
    }
};

int main() {
    // ============ unique_ptr ============
    
    cout << "===== unique_ptr =====" << endl;
    
    // 独占所有权的智能指针
    // 不能复制,只能移动
    
    // 创建方式1
    unique_ptr<Resource> ptr1(new Resource("资源1"));
    
    // 创建方式2(推荐)
    auto ptr2 = make_unique<Resource>("资源2");
    
    // 使用
    ptr1->use();
    ptr2->use();
    
    // 获取原始指针
    Resource* raw = ptr1.get();
    cout << "原始指针: " << raw->name << endl;
    
    // 移动所有权
    unique_ptr<Resource> ptr3 = move(ptr1);
    // ptr1现在是nullptr
    
    if (ptr1 == nullptr) {
        cout << "ptr1已转移所有权" << endl;
    }
    
    // 释放所有权
    Resource* released = ptr3.release();
    cout << "释放后需要手动删除" << endl;
    delete released;
    
    
    // ============ shared_ptr ============
    
    cout << "\n===== shared_ptr =====" << endl;
    
    // 共享所有权的智能指针
    // 引用计数,最后一个指针销毁时释放资源
    
    // 创建
    auto sptr1 = make_shared<Resource>("共享资源");
    
    cout << "引用计数: " << sptr1.use_count() << endl;  // 1
    
    {
        // 复制(增加引用计数)
        shared_ptr<Resource> sptr2 = sptr1;
        cout << "引用计数: " << sptr1.use_count() << endl;  // 2
        
        shared_ptr<Resource> sptr3 = sptr1;
        cout << "引用计数: " << sptr1.use_count() << endl;  // 3
        
        sptr2->use();
    }  // sptr2, sptr3离开作用域
    
    cout << "引用计数: " << sptr1.use_count() << endl;  // 1
    
    sptr1->use();
    // 离开作用域时自动销毁
    
    
    // ============ weak_ptr ============
    
    cout << "\n===== weak_ptr =====" << endl;
    
    // 弱引用,不增加引用计数
    // 用于解决shared_ptr的循环引用问题
    
    auto shared = make_shared<Resource>("弱引用资源");
    weak_ptr<Resource> weak = shared;
    
    cout << "shared引用计数: " << shared.use_count() << endl;  // 1
    cout << "weak引用计数: " << weak.use_count() << endl;    // 1(不影响)
    
    // 使用weak_ptr需要lock()
    if (auto locked = weak.lock()) {
        locked->use();  // 成功获取
    }
    
    shared.reset();  // 释放资源
    
    // 检查是否过期
    if (weak.expired()) {
        cout << "资源已释放" << endl;
    }
    
    if (auto locked = weak.lock()) {
        locked->use();
    } else {
        cout << "无法获取资源" << endl;
    }
    
    
    // ============ 智能指针选择 ============
    
    /*
     * 智能指针选择建议:
     * 
     * 1. unique_ptr:
     *    - 独占资源所有权
     *    - 性能最好,无额外开销
     *    - 默认选择
     * 
     * 2. shared_ptr:
     *    - 需要共享所有权时使用
     *    - 有引用计数开销
     * 
     * 3. weak_ptr:
     *    - 打破shared_ptr循环引用
     *    - 观察者模式
     *    - 缓存实现
     */
    
    return 0;
}

指针与函数

cpp
#include <iostream>
#include <memory>
using namespace std;

// ============ 指针作为参数 ============

// 通过指针修改值
void doubleValue(int* ptr) {
    if (ptr != nullptr) {
        *ptr *= 2;
    }
}

// 通过指针交换两个值
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// ============ 引用作为参数 ============

// 通过引用修改值(更简洁)
void doubleValueRef(int& ref) {
    ref *= 2;
}

// 通过引用交换
void swapRef(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

// ============ 返回指针 ============

// 返回局部变量的指针(危险!)
// int* badReturn() {
//     int local = 10;
//     return &local;  // 错误:返回局部变量的地址
// }

// 返回动态分配的内存
int* createArray(int size) {
    return new int[size];  // 调用者需要delete[]
}

// 返回智能指针(推荐)
unique_ptr<int[]> createArraySmart(int size) {
    return make_unique<int[]>(size);
}

// 返回静态变量的指针
int* getStaticValue() {
    static int value = 0;
    value++;
    return &value;  // 静态变量生命周期是整个程序
}

int main() {
    // 指针作为参数
    int num = 10;
    cout << "修改前: " << num << endl;
    doubleValue(&num);
    cout << "修改后: " << num << endl;
    
    // 交换
    int a = 5, b = 10;
    cout << "\n交换前: a = " << a << ", b = " << b << endl;
    swap(&a, &b);
    cout << "交换后: a = " << a << ", b = " << b << endl;
    
    // 引用作为参数
    num = 10;
    cout << "\n引用修改前: " << num << endl;
    doubleValueRef(num);
    cout << "引用修改后: " << num << endl;
    
    a = 5, b = 10;
    cout << "\n引用交换前: a = " << a << ", b = " << b << endl;
    swapRef(a, b);
    cout << "引用交换后: a = " << a << ", b = " << b << endl;
    
    // 返回指针
    int* arr = createArray(5);
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
    }
    cout << "\n动态数组: ";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    delete[] arr;
    
    // 智能指针
    auto smartArr = createArraySmart(5);
    for (int i = 0; i < 5; i++) {
        smartArr[i] = i + 1;
    }
    cout << "智能指针数组: ";
    for (int i = 0; i < 5; i++) {
        cout << smartArr[i] << " ";
    }
    cout << endl;
    // 自动释放
    
    // 静态变量指针
    cout << "\n静态变量: ";
    for (int i = 0; i < 3; i++) {
        int* p = getStaticValue();
        cout << *p << " ";
    }
    cout << endl;
    
    return 0;
}

本章小结

本章学习了:

  • 指针基础:定义、初始化、解引用
  • 指针运算:加减运算、指针与数组
  • const与指针:指向常量的指针、常量指针
  • 引用:引用的定义和特点
  • 智能指针:unique_ptr、shared_ptr、weak_ptr
  • 指针与函数:指针参数、返回指针

下一章,我们将学习字符串,了解C++字符串的处理方法。