C++指针全面知识点解析
指针是C++中最核心的概念之一,它提供了直接访问内存的能力,是理解C++内存模型和高效编程的基础。以下是关于C++指针的全面知识点解析:
一、指针的基本概念
指针是一个特殊的变量,它存储的是另一个变量的内存地址,而不是数据值本身。通过指针,我们可以间接访问和修改它所指向的变量。
#include <iostream>
using namespace std;
int main() {
int num = 42; // 声明一个整数变量
int* ptr; // 声明一个指向int类型的指针
ptr = # // 将num的地址赋值给指针ptr
// 输出各种信息
cout << "变量num的值: " << num << endl; // 直接访问变量值
cout << "变量num的地址: " << &num << endl; // 使用&获取变量地址
cout << "指针ptr存储的地址: " << ptr << endl; // 指针存储的地址
cout << "指针ptr指向的值: " << *ptr << endl; // 使用*解引用指针
return 0;
}
指针变量的大小在同一系统中是固定的,与它所指向的数据类型无关,通常为4字节(32位系统)或8字节(64位系统)。
二、指针的声明与初始化
指针的声明需要指定它所指向的数据类型,初始化是为了避免野指针。
int main() {
// 基本声明方式
int* p1; // 指向int的指针
char* p2; // 指向char的指针
double* p3; // 指向double的指针
// 初始化方式
int x = 10;
int* p4 = &x; // 初始化时指向变量x
int* p5 = nullptr; // C++11引入的空指针常量
int* p6 = 0; // 传统方式表示空指针
// int* p7 = NULL; // C风格的空指针,需要包含<cstdlib>
// 错误示例:未初始化的指针(野指针)
// int* p8;
// *p8 = 20; // 危险!可能导致程序崩溃或不可预测的行为
return 0;
}
注意:在C++11及以上版本,推荐使用
nullptr而非NULL或0,因为nullptr有明确的类型,能避免一些潜在的类型转换问题。
三、指针的解引用与赋值
解引用操作(*)用于访问指针所指向的变量的值,也可以通过解引用来修改该值。
int main() {
int a = 100;
int* ptr = &a;
cout << "a的初始值: " << a << endl; // 输出100
cout << "ptr解引用的值: " << *ptr << endl; // 输出100
// 通过指针修改变量值
*ptr = 200;
cout << "修改后a的值: " << a << endl; // 输出200
// 指针重新指向其他变量
int b = 300;
ptr = &b;
cout << "ptr现在指向的值: " << *ptr << endl; // 输出300
return 0;
}
注意:对空指针或野指针进行解引用是非常危险的操作,会导致未定义行为。
四、指针与数组
数组名本质上是指向数组第一个元素的常量指针,指针算术运算与数组访问密切相关。
int main() {
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // 数组名作为指针,指向第一个元素
// 数组访问的两种等价方式
cout << "数组元素访问:" << endl;
for (int i = 0; i < 5; i++) {
cout << "arr[" << i << "] = " << arr[i] << ", ";
cout << "*(ptr + " << i << ") = " << *(ptr + i) << endl;
}
// 指针算术运算
cout << "\n指针算术运算演示:" << endl;
cout << "ptr = " << ptr << ", *ptr = " << *ptr << endl; // 第一个元素
ptr++; // 指针向后移动一个int大小(通常4字节)
cout << "ptr++后: " << ptr << ", *ptr = " << *ptr << endl; // 第二个元素
ptr += 2; // 指针向后移动两个int大小
cout << "ptr += 2后: " << ptr << ", *ptr = " << *ptr << endl; // 第四个元素
return 0;
}
指针算术运算的特点:
ptr + n表示指针向后移动n个元素(不是n个字节)ptr - n表示指针向前移动n个元素- 两个同类型指针可以相减,结果是它们之间的元素个数
五、指针与函数
指针可以作为函数参数,实现函数对实参的修改;也可以作为函数返回值,返回动态分配的内存。
1. 指针作为函数参数
// 通过指针修改实参的值
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
cout << "交换前: x = " << x << ", y = " << y << endl; // 5, 10
swap(&x, &y); // 传递变量地址
cout << "交换后: x = " << x << ", y = " << y << endl; // 10, 5
return 0;
}
2. 指针作为函数返回值
// 函数返回指针
int* createArray(int size) {
return new int[size]; // 在堆上创建数组并返回指针
}
int main() {
int* arr = createArray(5);
// 给数组赋值
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
// 输出数组元素
for (int i = 0; i < 5; i++) {
cout << arr[i] << " "; // 0 10 20 30 40
}
delete[] arr; // 释放动态分配的内存
arr = nullptr; // 避免野指针
return 0;
}
六、const与指针
const与指针结合有多种形式,表达不同的含义,需要仔细区分:
int main() {
int a = 10, b = 20;
// 1. 指向常量的指针(pointer to const)
// 不能通过指针修改所指向的值,但可以改变指针指向
const int* ptr1 = &a;
// *ptr1 = 15; // 错误:不能修改指向的常量
ptr1 = &b; // 正确:可以改变指针指向
// 2. 常量指针(const pointer)
// 指针本身的值(地址)不能改变,但可以修改指向的值
int* const ptr2 = &a;
*ptr2 = 15; // 正确:可以修改指向的值
// ptr2 = &b; // 错误:不能改变指针指向
// 3. 指向常量的常量指针(const pointer to const)
// 既不能修改指向的值,也不能改变指针指向
const int* const ptr3 = &a;
// *ptr3 = 15; // 错误
// ptr3 = &b; // 错误
return 0;
}
记忆技巧:
const在*左边:指向的内容不可修改const在*右边:指针本身不可修改
七、指针数组与数组指针
这是两个容易混淆的概念,需要明确区分:
int main() {
int x = 1, y = 2, z = 3;
// 1. 指针数组:数组的每个元素都是指针
int* ptrArray[3] = {&x, &y, &z};
cout << "指针数组元素的值: ";
for (int i = 0; i < 3; i++) {
cout << *ptrArray[i] << " "; // 输出1 2 3
}
cout << endl;
// 2. 数组指针:指向整个数组的指针
int arr[3] = {10, 20, 30};
int (*arrPtr)[3] = &arr; // 注意括号的使用
// 访问数组元素的几种方式
cout << "数组指针访问元素: ";
cout << (*arrPtr)[0] << " "; // 10
cout << (*arrPtr)[1] << " "; // 20
cout << (*arrPtr)[2] << endl; // 30
// 通过数组指针遍历数组
for (int i = 0; i < 3; i++) {
cout << arrPtr[0][i] << " "; // 10 20 30
}
cout << endl;
return 0;
}
区分技巧:
- 指针数组:
int* arr[3],优先级[]高于*,先形成数组,元素是指针 - 数组指针:
int (*ptr)[3],*与ptr结合,形成指向数组的指针
八、多级指针
指针可以指向另一个指针,形成多级指针,最常用的是二级指针。
int main() {
int var = 50;
int* ptr = &var; // 一级指针,指向var
int** pptr = &ptr; // 二级指针,指向ptr
int*** ppptr = &pptr; // 三级指针,指向pptr
// 访问变量值
cout << "var = " << var << endl;
cout << "*ptr = " << *ptr << endl;
cout << "**pptr = " << **pptr << endl;
cout << "***ppptr = " << ***ppptr << endl;
// 访问地址
cout << "\n&var = " << &var << endl;
cout << "ptr = " << ptr << ", *pptr = " << *pptr << endl;
cout << "pptr = " << pptr << ", *ppptr = " << *ppptr << endl;
return 0;
}
多级指针常用于:
- 函数中修改指针的值
- 处理二维或更高维的动态数组
- 复杂数据结构中(如树、图等)
九、指针与动态内存分配
C++使用new和delete进行动态内存管理,它们通常与指针配合使用。
int main() {
// 1. 动态分配单个变量
int* single = new int; // 分配int大小的内存
*single = 100;
cout << "动态分配的单个变量: " << *single << endl;
delete single; // 释放内存
single = nullptr; // 置空指针,避免野指针
// 2. 动态分配并初始化变量
double* dbl = new double(3.14159);
cout << "动态初始化的变量: " << *dbl << endl;
delete dbl;
dbl = nullptr;
// 3. 动态分配数组
int size = 5;
int* arr = new int[size]; // 分配数组
for (int i = 0; i < size; i++) {
arr[i] = i + 1;
}
cout << "动态数组元素: ";
for (int i = 0; i < size; i++) {
cout << arr[i] << " "; // 1 2 3 4 5
}
cout << endl;
delete[] arr; // 释放数组内存,注意使用[]
arr = nullptr;
return 0;
}
动态内存分配注意事项:
- 用
new分配的内存必须用delete释放(数组用delete[]) - 避免内存泄漏:确保每个
new都有对应的delete - 释放后将指针置空,避免成为野指针
- 不要重复释放同一内存块
十、函数指针
函数指针是指向函数的指针,可以像普通指针一样使用,常用于回调函数。
// 定义几个函数
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
// 接受函数指针作为参数的函数
void calculateAndPrint(int x, int y, int (*operation)(int, int)) {
int result = operation(x, y);
cout << "结果: " << result << endl;
}
int main() {
// 声明函数指针并赋值
int (*funcPtr)(int, int);
funcPtr = add; // 指向add函数
cout << "3 + 5 = " << funcPtr(3, 5) << endl; // 输出8
funcPtr = multiply; // 指向multiply函数
cout << "3 * 5 = " << funcPtr(3, 5) << endl; // 输出15
// 使用函数指针作为回调函数
cout << "使用回调函数: ";
calculateAndPrint(4, 6, add); // 输出10
cout << "使用回调函数: ";
calculateAndPrint(4, 6, multiply); // 输出24
return 0;
}
函数指针的应用场景:
- 回调函数(如排序算法中的比较函数)
- 实现状态机
- 模拟多态(在C语言中常用)
- 事件处理机制
十一、指针的常见问题与最佳实践
1. 野指针(Wild Pointer)
未初始化或已释放的指针,指向未知内存区域:
// 错误示例
int* ptr; // 未初始化的野指针
*ptr = 10; // 危险!可能导致程序崩溃
2. 空指针解引用(Null Pointer Dereference)
对nullptr或0进行解引用操作:
// 错误示例
int* ptr = nullptr;
cout << *ptr << endl; // 错误:解引用空指针
3. 内存泄漏(Memory Leak)
动态分配的内存未被释放,导致内存资源浪费:
// 错误示例
void badFunction() {
int* data = new int[100];
// 没有对应的delete[],导致内存泄漏
}
4. 悬垂指针(Dangling Pointer)
指针指向的内存已被释放,但指针未被置空:
// 错误示例
int* ptr = new int;
delete ptr;
// ptr现在是悬垂指针
*ptr = 10; // 危险!访问已释放的内存
最佳实践:
- 指针声明时立即初始化,或初始化为
nullptr - 解引用指针前检查是否为
nullptr - 动态分配的内存确保被释放,且只释放一次
- 释放内存后将指针置为
nullptr - 尽量使用智能指针(如
std::unique_ptr、std::shared_ptr)替代裸指针 - 避免使用多级指针,必要时考虑其他数据结构
掌握指针是C++编程的重要里程碑,它既有强大的功能,也伴随着风险。通过大量实践和遵循最佳实践,可以安全有效地使用指针,编写出高效、灵活的C++代码。
更多推荐


所有评论(0)