Skip to content

C++ 指针

概述

指针是C++中最强大也最具挑战性的特性之一。指针变量存储的是另一个变量的内存地址,而不是值本身。通过指针,我们可以间接访问和操作内存中的数据,实现动态内存分配、高效的参数传递和复杂的数据结构。

🎯 指针基础概念

指针的声明和初始化

cpp
#include <iostream>

int main() {
    std::cout << "=== 指针基础 ===" << std::endl;
    
    // 1. 指针声明
    int* ptr1;           // 声明一个指向int的指针
    double* ptr2;        // 声明一个指向double的指针
    char* ptr3;          // 声明一个指向char的指针
    
    // 2. 指针初始化
    int num = 42;
    int* ptr = &num;     // ptr指向num的地址
    
    // 输出地址和值
    std::cout << "变量num的值: " << num << std::endl;
    std::cout << "变量num的地址: " << &num << std::endl;
    std::cout << "指针ptr的值(存储的地址): " << ptr << std::endl;
    std::cout << "指针ptr指向的值: " << *ptr << std::endl;
    std::cout << "指针ptr本身的地址: " << &ptr << std::endl;
    
    // 3. 空指针
    int* null_ptr = nullptr;  // C++11推荐使用nullptr
    int* old_null = NULL;     // 传统方式
    int* zero_ptr = 0;        // 也可以,但不推荐
    
    std::cout << "\n空指针值: " << null_ptr << std::endl;
    
    // 4. 指针类型很重要
    double value = 3.14;
    double* d_ptr = &value;
    // int* wrong_ptr = &value;  // 错误!类型不匹配
    
    std::cout << "double值: " << *d_ptr << std::endl;
    
    return 0;
}

指针运算符

cpp
#include <iostream>

int main() {
    std::cout << "=== 指针运算符 ===" << std::endl;
    
    int number = 100;
    int* ptr = &number;
    
    // 1. 取地址运算符 &
    std::cout << "取地址 &number: " << &number << std::endl;
    
    // 2. 解引用运算符 *
    std::cout << "解引用 *ptr: " << *ptr << std::endl;
    
    // 3. 通过指针修改值
    *ptr = 200;
    std::cout << "修改后的number: " << number << std::endl;
    std::cout << "修改后的*ptr: " << *ptr << std::endl;
    
    // 4. 指针的指针
    int** ptr_to_ptr = &ptr;
    std::cout << "\n指针的指针:" << std::endl;
    std::cout << "ptr_to_ptr的值: " << ptr_to_ptr << std::endl;
    std::cout << "*ptr_to_ptr的值: " << *ptr_to_ptr << std::endl;
    std::cout << "**ptr_to_ptr的值: " << **ptr_to_ptr << std::endl;
    
    // 5. void指针(通用指针)
    void* void_ptr = &number;
    std::cout << "\nvoid指针: " << void_ptr << std::endl;
    // std::cout << *void_ptr;  // 错误!void指针不能直接解引用
    
    // 需要类型转换
    int* typed_ptr = static_cast<int*>(void_ptr);
    std::cout << "转换后解引用: " << *typed_ptr << std::endl;
    
    return 0;
}

📊 指针和数组

数组指针关系

cpp
#include <iostream>

int main() {
    std::cout << "=== 指针和数组 ===" << std::endl;
    
    int arr[5] = {10, 20, 30, 40, 50};
    
    // 1. 数组名就是指向第一个元素的指针
    int* ptr = arr;  // 等价于 int* ptr = &arr[0];
    
    std::cout << "数组名arr: " << arr << std::endl;
    std::cout << "指针ptr: " << ptr << std::endl;
    std::cout << "第一个元素地址&arr[0]: " << &arr[0] << std::endl;
    
    // 2. 通过指针访问数组元素
    std::cout << "\n通过指针访问数组:" << std::endl;
    for (int i = 0; i < 5; i++) {
        std::cout << "arr[" << i << "] = " << arr[i] 
                  << ", *(ptr + " << i << ") = " << *(ptr + i) << std::endl;
    }
    
    // 3. 指针算术
    std::cout << "\n指针算术:" << std::endl;
    ptr = arr;  // 重置指针到数组开始
    
    std::cout << "ptr指向: " << *ptr << std::endl;
    ptr++;  // 指向下一个元素
    std::cout << "ptr++后指向: " << *ptr << std::endl;
    ptr += 2;  // 向前移动2个位置
    std::cout << "ptr += 2后指向: " << *ptr << std::endl;
    
    // 4. 指针和下标的等价性
    ptr = arr;
    std::cout << "\n指针和下标等价性:" << std::endl;
    std::cout << "ptr[2] = " << ptr[2] << std::endl;
    std::cout << "*(ptr + 2) = " << *(ptr + 2) << std::endl;
    std::cout << "arr[2] = " << arr[2] << std::endl;
    
    return 0;
}

指针数组和数组指针

cpp
#include <iostream>

int main() {
    std::cout << "=== 指针数组 vs 数组指针 ===" << std::endl;
    
    // 1. 指针数组:数组的元素是指针
    int a = 10, b = 20, c = 30;
    int* ptr_array[3] = {&a, &b, &c};
    
    std::cout << "指针数组:" << std::endl;
    for (int i = 0; i < 3; i++) {
        std::cout << "ptr_array[" << i << "] 指向的值: " << *ptr_array[i] << std::endl;
    }
    
    // 2. 数组指针:指向数组的指针
    int arr[3] = {100, 200, 300};
    int (*array_ptr)[3] = &arr;  // 指向包含3个int的数组
    
    std::cout << "\n数组指针:" << std::endl;
    for (int i = 0; i < 3; i++) {
        std::cout << "(*array_ptr)[" << i << "] = " << (*array_ptr)[i] << std::endl;
    }
    
    // 3. 字符串数组
    const char* str_array[] = {
        "Hello",
        "World", 
        "C++",
        "Programming"
    };
    
    std::cout << "\n字符串数组:" << std::endl;
    for (int i = 0; i < 4; i++) {
        std::cout << "str_array[" << i << "]: " << str_array[i] << std::endl;
    }
    
    // 4. 二维数组和指针
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int (*row_ptr)[3] = matrix;  // 指向包含3个int的数组
    
    std::cout << "\n二维数组访问:" << std::endl;
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            std::cout << "row_ptr[" << i << "][" << j << "] = " 
                      << row_ptr[i][j] << " ";
        }
        std::cout << std::endl;
    }
    
    return 0;
}

🔗 指针和函数

指针作为函数参数

cpp
#include <iostream>

// 值传递
void modifyByValue(int x) {
    x = 100;
    std::cout << "函数内x = " << x << std::endl;
}

// 指针传递
void modifyByPointer(int* x) {
    if (x != nullptr) {
        *x = 100;
        std::cout << "函数内*x = " << *x << std::endl;
    }
}

// 交换两个变量
void swap(int* a, int* b) {
    if (a != nullptr && b != nullptr) {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
}

// 数组参数(实际是指针)
void printArray(int* arr, int size) {
    std::cout << "函数内sizeof(arr) = " << sizeof(arr) << " (指针大小)" << std::endl;
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

// 修改数组元素
void doubleArray(int* arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;
    }
}

int main() {
    std::cout << "=== 指针作为函数参数 ===" << std::endl;
    
    // 1. 值传递vs指针传递
    int num = 42;
    
    std::cout << "调用前num = " << num << std::endl;
    
    modifyByValue(num);
    std::cout << "值传递后num = " << num << std::endl;
    
    modifyByPointer(&num);
    std::cout << "指针传递后num = " << num << std::endl;
    
    // 2. 交换变量
    int x = 10, y = 20;
    std::cout << "\n交换前: x = " << x << ", y = " << y << std::endl;
    swap(&x, &y);
    std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
    
    // 3. 数组参数
    int arr[5] = {1, 2, 3, 4, 5};
    
    std::cout << "\nmain中sizeof(arr) = " << sizeof(arr) << std::endl;
    std::cout << "原始数组: ";
    printArray(arr, 5);
    
    doubleArray(arr, 5);
    std::cout << "翻倍后: ";
    printArray(arr, 5);
    
    return 0;
}

函数指针

cpp
#include <iostream>

// 普通函数
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

int subtract(int a, int b) {
    return a - b;
}

// 接受函数指针作为参数的函数
int calculate(int x, int y, int (*operation)(int, int)) {
    return operation(x, y);
}

// 返回函数指针的函数
int (*getOperation(char op))(int, int) {
    switch (op) {
        case '+': return add;
        case '*': return multiply;
        case '-': return subtract;
        default: return nullptr;
    }
}

int main() {
    std::cout << "=== 函数指针 ===" << std::endl;
    
    // 1. 基本函数指针
    int (*func_ptr)(int, int) = add;
    
    std::cout << "函数指针调用: " << func_ptr(5, 3) << std::endl;
    
    // 2. 函数指针数组
    int (*operations[])(int, int) = {add, multiply, subtract};
    const char* op_names[] = {"加法", "乘法", "减法"};
    
    std::cout << "\n函数指针数组:" << std::endl;
    for (int i = 0; i < 3; i++) {
        std::cout << op_names[i] << ": " << operations[i](8, 4) << std::endl;
    }
    
    // 3. 高阶函数
    std::cout << "\n高阶函数:" << std::endl;
    std::cout << "calculate(10, 5, add): " << calculate(10, 5, add) << std::endl;
    std::cout << "calculate(10, 5, multiply): " << calculate(10, 5, multiply) << std::endl;
    
    // 4. 返回函数指针
    std::cout << "\n动态选择函数:" << std::endl;
    char ops[] = {'+', '*', '-'};
    
    for (char op : ops) {
        auto func = getOperation(op);
        if (func != nullptr) {
            std::cout << "运算 " << op << ": " << func(12, 3) << std::endl;
        }
    }
    
    return 0;
}

💾 动态内存管理

new和delete操作符

cpp
#include <iostream>

int main() {
    std::cout << "=== 动态内存分配 ===" << std::endl;
    
    // 1. 分配单个变量
    int* ptr = new int(42);  // 分配并初始化
    std::cout << "动态分配的int值: " << *ptr << std::endl;
    
    // 使用完毕后释放
    delete ptr;
    ptr = nullptr;  // 防止悬挂指针
    
    // 2. 分配数组
    int size = 5;
    int* arr = new int[size];  // 分配数组
    
    // 初始化数组
    for (int i = 0; i < size; i++) {
        arr[i] = (i + 1) * 10;
    }
    
    std::cout << "动态数组: ";
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    // 释放数组
    delete[] arr;
    arr = nullptr;
    
    // 3. 二维动态数组
    int rows = 3, cols = 4;
    
    // 分配指针数组
    int** matrix = new int*[rows];
    for (int i = 0; i < rows; i++) {
        matrix[i] = new int[cols];
    }
    
    // 初始化
    int value = 1;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = value++;
        }
    }
    
    // 打印矩阵
    std::cout << "\n动态二维数组:" << std::endl;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            std::cout << matrix[i][j] << "\t";
        }
        std::cout << std::endl;
    }
    
    // 释放二维数组
    for (int i = 0; i < rows; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;
    matrix = nullptr;
    
    // 4. 错误处理
    try {
        // 分配大量内存可能失败
        int* huge_array = new int[1000000000];
        delete[] huge_array;
    }
    catch (const std::bad_alloc& e) {
        std::cout << "内存分配失败: " << e.what() << std::endl;
    }
    
    return 0;
}

内存泄漏和悬挂指针

cpp
#include <iostream>

// 演示内存泄漏的函数
void memoryLeakExample() {
    int* ptr = new int(100);
    // 忘记delete ptr; 导致内存泄漏
    std::cout << "值: " << *ptr << std::endl;
    // 函数结束,ptr销毁,但它指向的内存没有释放
}

// 演示悬挂指针的函数
int* danglingPointerExample() {
    int* ptr = new int(200);
    delete ptr;
    return ptr;  // 返回已释放的指针
}

// 正确的内存管理
class SafeArray {
private:
    int* data_;
    int size_;
    
public:
    SafeArray(int size) : size_(size) {
        data_ = new int[size_];
        std::cout << "分配了 " << size_ << " 个int的内存" << std::endl;
    }
    
    ~SafeArray() {
        delete[] data_;
        std::cout << "释放了内存" << std::endl;
    }
    
    int& operator[](int index) {
        return data_[index];
    }
    
    int getSize() const { return size_; }
};

int main() {
    std::cout << "=== 内存管理问题 ===" << std::endl;
    
    // 1. 内存泄漏示例
    std::cout << "1. 内存泄漏示例:" << std::endl;
    memoryLeakExample();
    std::cout << "函数结束,但内存没有释放" << std::endl;
    
    // 2. 悬挂指针示例
    std::cout << "\n2. 悬挂指针示例:" << std::endl;
    int* dangerous_ptr = danglingPointerExample();
    // std::cout << *dangerous_ptr;  // 危险!访问已释放的内存
    std::cout << "dangerous_ptr指向已释放的内存" << std::endl;
    
    // 3. 正确的做法
    std::cout << "\n3. 正确的内存管理:" << std::endl;
    {
        SafeArray arr(5);
        for (int i = 0; i < arr.getSize(); i++) {
            arr[i] = i * i;
            std::cout << arr[i] << " ";
        }
        std::cout << std::endl;
        // 作用域结束,自动调用析构函数释放内存
    }
    
    // 4. 最佳实践
    std::cout << "\n4. 最佳实践:" << std::endl;
    int* ptr = new int(300);
    
    // 使用完立即释放
    std::cout << "值: " << *ptr << std::endl;
    delete ptr;
    ptr = nullptr;  // 设置为nullptr防止误用
    
    // 检查nullptr
    if (ptr != nullptr) {
        std::cout << "指针有效" << std::endl;
    } else {
        std::cout << "指针为nullptr,安全" << std::endl;
    }
    
    return 0;
}

🔧 智能指针 (C++11)

unique_ptr和shared_ptr

cpp
#include <iostream>
#include <memory>

class Resource {
private:
    int id_;
    
public:
    Resource(int id) : id_(id) {
        std::cout << "Resource " << id_ << " 创建" << std::endl;
    }
    
    ~Resource() {
        std::cout << "Resource " << id_ << " 销毁" << std::endl;
    }
    
    void doSomething() {
        std::cout << "Resource " << id_ << " 执行操作" << std::endl;
    }
    
    int getId() const { return id_; }
};

int main() {
    std::cout << "=== 智能指针 ===" << std::endl;
    
    // 1. unique_ptr - 独占所有权
    std::cout << "1. unique_ptr示例:" << std::endl;
    {
        std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>(1);
        ptr1->doSomething();
        
        // 移动所有权
        std::unique_ptr<Resource> ptr2 = std::move(ptr1);
        std::cout << "ptr1是否为空: " << (ptr1 == nullptr) << std::endl;
        
        if (ptr2) {
            ptr2->doSomething();
        }
        // 作用域结束,自动释放资源
    }
    
    // 2. shared_ptr - 共享所有权
    std::cout << "\n2. shared_ptr示例:" << std::endl;
    {
        std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(2);
        std::cout << "引用计数: " << ptr1.use_count() << std::endl;
        
        {
            std::shared_ptr<Resource> ptr2 = ptr1;  // 拷贝,引用计数增加
            std::cout << "拷贝后引用计数: " << ptr1.use_count() << std::endl;
            
            ptr2->doSomething();
            // ptr2作用域结束,引用计数减少
        }
        
        std::cout << "ptr2销毁后引用计数: " << ptr1.use_count() << std::endl;
        // ptr1作用域结束,引用计数为0,资源被释放
    }
    
    // 3. weak_ptr - 避免循环引用
    std::cout << "\n3. weak_ptr示例:" << std::endl;
    {
        std::shared_ptr<Resource> shared = std::make_shared<Resource>(3);
        std::weak_ptr<Resource> weak = shared;
        
        std::cout << "shared引用计数: " << shared.use_count() << std::endl;
        std::cout << "weak是否过期: " << weak.expired() << std::endl;
        
        // 通过weak_ptr访问资源
        if (auto locked = weak.lock()) {
            locked->doSomething();
        }
        
        shared.reset();  // 释放shared_ptr
        std::cout << "shared释放后,weak是否过期: " << weak.expired() << std::endl;
    }
    
    // 4. 智能指针数组
    std::cout << "\n4. 智能指针数组:" << std::endl;
    {
        std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);
        for (int i = 0; i < 5; i++) {
            arr[i] = i * i;
            std::cout << arr[i] << " ";
        }
        std::cout << std::endl;
        // 自动释放数组
    }
    
    return 0;
}

📋 指针最佳实践

安全指针使用

cpp
#include <iostream>
#include <memory>

// 指针工具函数
namespace PointerUtils {
    // 安全删除指针
    template<typename T>
    void safeDelete(T*& ptr) {
        delete ptr;
        ptr = nullptr;
    }
    
    // 安全删除数组指针
    template<typename T>
    void safeDeleteArray(T*& ptr) {
        delete[] ptr;
        ptr = nullptr;
    }
    
    // 检查指针有效性
    template<typename T>
    bool isValidPointer(T* ptr) {
        return ptr != nullptr;
    }
}

// RAII类示例
class Buffer {
private:
    char* data_;
    size_t size_;
    
public:
    Buffer(size_t size) : size_(size) {
        data_ = new char[size_];
        std::cout << "Buffer分配 " << size_ << " 字节" << std::endl;
    }
    
    ~Buffer() {
        delete[] data_;
        std::cout << "Buffer释放内存" << std::endl;
    }
    
    // 禁止拷贝
    Buffer(const Buffer&) = delete;
    Buffer& operator=(const Buffer&) = delete;
    
    // 移动构造
    Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }
    
    char* getData() { return data_; }
    size_t getSize() const { return size_; }
};

int main() {
    std::cout << "=== 指针最佳实践 ===" << std::endl;
    
    // 1. 使用智能指针替代原始指针
    std::cout << "1. 推荐使用智能指针:" << std::endl;
    {
        auto resource = std::make_unique<int>(42);
        std::cout << "智能指针值: " << *resource << std::endl;
        // 自动释放,无需手动管理
    }
    
    // 2. 如果必须使用原始指针,确保正确释放
    std::cout << "\n2. 原始指针安全使用:" << std::endl;
    int* raw_ptr = new int(100);
    
    try {
        std::cout << "原始指针值: " << *raw_ptr << std::endl;
        PointerUtils::safeDelete(raw_ptr);
    }
    catch (...) {
        PointerUtils::safeDelete(raw_ptr);  // 异常安全
        throw;
    }
    
    // 3. RAII原则
    std::cout << "\n3. RAII原则示例:" << std::endl;
    {
        Buffer buffer(1024);
        // 使用buffer...
        // 作用域结束自动释放
    }
    
    // 4. 指针检查
    std::cout << "\n4. 指针安全检查:" << std::endl;
    int* test_ptr = nullptr;
    
    if (PointerUtils::isValidPointer(test_ptr)) {
        std::cout << "指针有效" << std::endl;
    } else {
        std::cout << "指针无效,避免解引用" << std::endl;
    }
    
    // 5. 函数参数指针检查
    auto safeFunction = [](int* ptr) {
        if (ptr == nullptr) {
            std::cout << "传入空指针,函数安全返回" << std::endl;
            return;
        }
        std::cout << "安全处理指针值: " << *ptr << std::endl;
    };
    
    safeFunction(nullptr);
    int value = 123;
    safeFunction(&value);
    
    return 0;
}

总结

指针是C++的核心特性,提供了强大的内存管理和数据访问能力:

核心概念

  • 指针基础:地址、解引用、指针算术
  • 指针与数组:数组名即指针、指针算术
  • 函数指针:函数地址、回调机制
  • 动态内存:new/delete、内存管理

现代C++推荐

传统做法现代替代优势
原始指针智能指针自动内存管理
new/deletemake_unique/make_shared异常安全
手动内存管理RAII资源安全

最佳实践

  • 优先使用智能指针而非原始指针
  • 遵循RAII原则管理资源
  • 始终检查指针有效性
  • 避免悬挂指针和内存泄漏
  • 使用nullptr而非NULL或0

安全指针使用原则

  1. 获取资源即初始化(RAII)
  2. 谁分配谁释放
  3. 异常安全保证
  4. 避免所有权模糊
  5. 使用工具检测内存问题

指针是系统编程、性能优化和复杂数据结构的基础,正确掌握指针使用是成为C++高手的必经之路。

本站内容仅供学习和研究使用。