指针是 C++ 中最重要也是最具挑战性的概念之一。指针是一个变量,它存储另一个变量的内存地址。理解指针对于掌握 C++ 至关重要。
指针的基本概念
什么是指针
#include <iostream>
int main() {
// 普通变量
int x = 42;
// 指针变量:存储其他变量的地址
int* ptr = &x; // ptr 指向 x 的地址
std::cout << "变量 x 的值: " << x << std::endl;
std::cout << "变量 x 的地址: " << &x << std::endl;
std::cout << "指针 ptr 的值(x的地址): " << ptr << std::endl;
std::cout << "指针 ptr 指向的值: " << *ptr << std::endl;
std::cout << "指针 ptr 自身的地址: " << &ptr << std::endl;
// 内存布局示意
std::cout << "\n内存布局:" << std::endl;
std::cout << "地址\t\t变量\t值" << std::endl;
std::cout << &x << "\tx\t" << x << std::endl;
std::cout << &ptr << "\tptr\t" << ptr << std::endl;
return 0;
}
指针的声明和初始化
#include <iostream>
int main() {
// 1. 指针声明的不同方式
int* ptr1; // 推荐:* 靠近类型
int *ptr2; // 也可以:* 靠近变量名
int * ptr3; // 也可以:* 两边都有空格
// 2. 多个指针声明
int *p1, *p2; // p1 和 p2 都是指针
int* p3, p4; // 注意:p3 是指针,p4 是普通 int 变量!
// 3. 指针初始化
int value = 100;
int* ptr = &value; // 用变量地址初始化
int* ptr_null = nullptr; // C++11:空指针
int* ptr_zero = 0; // 传统:用 0 初始化(不推荐)
int* ptr_NULL = NULL; // C 风格:用 NULL 初始化(不推荐)
// 4. 未初始化的指针(危险!)
int* dangerous_ptr; // 包含垃圾值,使用前必须初始化
// std::cout << *dangerous_ptr; // 未定义行为!
std::cout << "value = " << value << std::endl;
std::cout << "ptr 指向的值 = " << *ptr << std::endl;
std::cout << "ptr_null = " << ptr_null << std::endl;
return 0;
}
指针的基本操作
取地址运算符和解引用运算符
#include <iostream>
int main() {
int x = 10;
int y = 20;
// 取地址运算符 &
int* ptr = &x;
std::cout << "初始状态:" << std::endl;
std::cout << "x = " << x << ", 地址 = " << &x << std::endl;
std::cout << "y = " << y << ", 地址 = " << &y << std::endl;
std::cout << "ptr = " << ptr << std::endl;
// 解引用运算符 *
std::cout << "\n通过指针访问:" << std::endl;
std::cout << "*ptr = " << *ptr << std::endl;
// 通过指针修改值
*ptr = 50;
std::cout << "\n修改 *ptr = 50 后:" << std::endl;
std::cout << "x = " << x << std::endl; // x 也变成了 50
std::cout << "*ptr = " << *ptr << std::endl;
// 改变指针指向
ptr = &y;
std::cout << "\n改变指针指向 y 后:" << std::endl;
std::cout << "ptr = " << ptr << std::endl;
std::cout << "*ptr = " << *ptr << std::endl;
// 通过新指向修改值
*ptr = 100;
std::cout << "\n修改 *ptr = 100 后:" << std::endl;
std::cout << "y = " << y << std::endl; // y 变成了 100
std::cout << "x = " << x << std::endl; // x 保持 50 不变
return 0;
}
指针运算
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // 指向数组第一个元素
std::cout << "数组元素和地址:" << std::endl;
for (int i = 0; i < 5; ++i) {
std::cout << "arr[" << i << "] = " << arr[i]
<< ", 地址 = " << &arr[i] << std::endl;
}
std::cout << "\n指针运算:" << std::endl;
// 指针递增
std::cout << "ptr 指向 arr[0]: " << *ptr << std::endl;
ptr++; // 移动到下一个元素
std::cout << "ptr++ 后指向 arr[1]: " << *ptr << std::endl;
// 指针加法
ptr = arr; // 重置到开始
std::cout << "*(ptr + 2) = " << *(ptr + 2) << std::endl; // arr[2]
std::cout << "*(ptr + 4) = " << *(ptr + 4) << std::endl; // arr[4]
// 指针减法
int* ptr1 = &arr[4];
int* ptr2 = &arr[1];
ptrdiff_t diff = ptr1 - ptr2; // 指针之间的距离
std::cout << "ptr1 - ptr2 = " << diff << " 个元素" << std::endl;
// 指针比较
if (ptr1 > ptr2) {
std::cout << "ptr1 指向更高的内存地址" << std::endl;
}
// 数组下标等价于指针运算
ptr = arr;
std::cout << "\n数组下标 vs 指针运算:" << std::endl;
for (int i = 0; i < 5; ++i) {
std::cout << "arr[" << i << "] = " << arr[i]
<< ", *(ptr + " << i << ") = " << *(ptr + i) << std::endl;
}
return 0;
}
指针与数组
数组名作为指针
#include <iostream>
void printArray(int* arr, int size) {
std::cout << "在函数中:" << std::endl;
std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 指针大小
for (int i = 0; i < size; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
std::cout << "在 main 中:" << std::endl;
std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 整个数组大小
// 数组名可以隐式转换为指向第一个元素的指针
int* ptr = arr; // 等价于 int* ptr = &arr[0];
std::cout << "arr = " << arr << std::endl;
std::cout << "&arr[0] = " << &arr[0] << std::endl;
std::cout << "ptr = " << ptr << std::endl;
// 数组名和指针的区别
std::cout << "\n数组名 vs 指针:" << std::endl;
std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20 (5 * 4)
std::cout << "sizeof(ptr) = " << sizeof(ptr) << std::endl; // 8 (64位系统)
// 数组名不能被赋值(它是常量)
// arr = ptr; // 错误!
ptr = arr; // 正确
// 传递数组给函数
printArray(arr, 5); // 数组退化为指针
printArray(ptr, 5); // 直接传递指针
return 0;
}
指针数组 vs 数组指针
#include <iostream>
int main() {
// 1. 指针数组:数组的每个元素都是指针
int a = 10, b = 20, c = 30;
int* ptrArray[3] = {&a, &b, &c}; // 3 个指针的数组
std::cout << "指针数组:" << std::endl;
for (int i = 0; i < 3; ++i) {
std::cout << "ptrArray[" << i << "] = " << ptrArray[i]
<< ", *ptrArray[" << i << "] = " << *ptrArray[i] << std::endl;
}
// 2. 数组指针:指向数组的指针
int arr[3] = {100, 200, 300};
int (*arrayPtr)[3] = &arr; // 指向包含3个int的数组的指针
std::cout << "\n数组指针:" << std::endl;
std::cout << "arrayPtr = " << arrayPtr << std::endl;
std::cout << "*arrayPtr = " << *arrayPtr << std::endl; // 指向数组第一个元素
// 通过数组指针访问元素
for (int i = 0; i < 3; ++i) {
std::cout << "(*arrayPtr)[" << i << "] = " << (*arrayPtr)[i] << std::endl;
}
// 3. 字符串指针数组
const char* strings[] = {"Hello", "World", "C++"};
std::cout << "\n字符串指针数组:" << std::endl;
for (int i = 0; i < 3; ++i) {
std::cout << "strings[" << i << "] = " << strings[i] << std::endl;
}
// 4. 二维数组和指针
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*matrixPtr)[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 << "matrixPtr[" << i << "][" << j << "] = "
<< matrixPtr[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
动态内存分配
new 和 delete 操作符
#include <iostream>
int main() {
// 1. 动态分配单个变量
int* ptr = new int(42); // 分配并初始化
std::cout << "动态分配的值: " << *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 * 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;
// 方法1:指针数组
int** matrix1 = new int*[rows];
for (int i = 0; i < rows; ++i) {
matrix1[i] = new int[cols];
}
// 初始化
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
matrix1[i][j] = i * cols + j;
}
}
std::cout << "动态二维数组:" << std::endl;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << matrix1[i][j] << "\t";
}
std::cout << std::endl;
}
// 释放二维数组
for (int i = 0; i < rows; ++i) {
delete[] matrix1[i];
}
delete[] matrix1;
// 方法2:一维数组模拟二维
int* matrix2 = new int[rows * cols];
for (int i = 0; i < rows * cols; ++i) {
matrix2[i] = i;
}
std::cout << "一维数组模拟二维:" << std::endl;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << matrix2[i * cols + j] << "\t";
}
std::cout << std::endl;
}
delete[] matrix2;
return 0;
}
内存泄漏和悬空指针
#include <iostream>
void demonstrateMemoryLeaks() {
// 1. 内存泄漏示例
int* ptr1 = new int(100);
ptr1 = new int(200); // 内存泄漏!原来的内存没有释放
delete ptr1; // 只释放了第