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 = # // 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/delete | make_unique/make_shared | 异常安全 |
| 手动内存管理 | RAII | 资源安全 |
最佳实践
- 优先使用智能指针而非原始指针
- 遵循RAII原则管理资源
- 始终检查指针有效性
- 避免悬挂指针和内存泄漏
- 使用nullptr而非NULL或0
安全指针使用原则
- 获取资源即初始化(RAII)
- 谁分配谁释放
- 异常安全保证
- 避免所有权模糊
- 使用工具检测内存问题
指针是系统编程、性能优化和复杂数据结构的基础,正确掌握指针使用是成为C++高手的必经之路。