C++核心特性:指针的概念与使用

一、基本概念

指针是C++中极具代表性的特性,也是掌握C++高效内存操作的关键。简单来说,指针是一种存储变量内存地址的变量。我们平时使用的普通变量存储的是数据本身,而指针存储的是数据在内存中的“位置”(即地址),通过这个地址就能找到并操作对应的数据

二、指针的定义与初始化

指针的定义格式为“数据类型* 指针变量名;”,其中“*”是指针的标志,表明该变量是指针类型。需要注意的是,指针的“数据类型”必须与它指向的变量的数据类型一致,因为不同类型的变量在内存中占用的字节数不同,指针需要知道如何解析地址对应的内存。

指针初始化有两种常见方式:一是将已定义变量的地址赋值给指针(用“&”取地址运算符);二是使用“null ptr” 初始化空指针,避免野指针(指向不确定内存的指针,会导致程序崩溃)



// 指针定义与初始化示例
#include <iostream>
using namespace std;
 
int main() {
    int num = 100;         // 普通整型变量,存储数据100
    int* p_num = &num;     // 定义int型指针p_num,指向num的地址(&取地址)
    char* p_char = nullptr;// 定义char型空指针,初始化为nullptr
 
    // 输出变量地址和指针存储的地址(两者相同)
    cout << "num的地址:" << &num << endl;
    cout << "p_num存储的地址:" << p_num << endl;
    return 0;
}

三、指针的核心操作:解引用

定义指针后,若要通过指针操作它指向的变量(比如读取或修改变量的值),就需要使用“解引用运算符*”。对指针使用“*”,就相当于直接操作指针指向的原变量。



// 指针解引用操作示例
#include <iostream>
using namespace std;
 
int main() {
    int a = 10;
    int* p_a = &a; // 指针p_a指向a
 
    cout << "原变量a的值:" << a << endl;         // 输出10
    cout << "通过指针获取a的值:" << *p_a << endl; // 解引用,输出10
 
    // 通过指针修改原变量的值
    *p_a = 20;
    cout << "修改后a的值:" << a << endl;         // 输出20,原变量被修改
    return 0;
}

“*p_a”表示“指针p_a指向的变量”,因此“*p_a = 20;”等价于“a = 20;”,直接修改了原变量a的值。这就是指针的核心作用——通过地址间接操作变量。

四、指针的常见使用场景:函数传参

C++中函数参数默认是“值传递”,即函数内部的参数是原变量的副本,修改副本不会影响原变量。而使用指针作为函数参数,可以实现“地址传递”,让函数直接操作主函数中的变量,这是指针最常用的场景之一。



// 指针作为函数参数示例:交换两个变量的值
#include <iostream>
using namespace std;
 
// 指针作为参数,接收两个int变量的地址
void swap(int* p1, int* p2) {
    int temp = *p1; // 取p1指向的值,存入temp
    *p1 = *p2;      // 把p2指向的值赋给p1指向的变量
    *p2 = temp;     // 把temp的值赋给p2指向的变量
}
 
int main() {
    int x = 5, y = 10;
    cout << "交换前:x=" << x << ", y=" << y << endl; // 输出x=5, y=10
 
    // 传入x和y的地址
    swap(&x, &y);
    cout << "交换后:x=" << x << ", y=" << y << endl; // 输出x=10, y=5
    return 0;
}

若不使用指针,普通的“值传递”交换函数只能交换函数内部副本的值,主函数中的x和y不会变化。而这里传入x和y的地址,函数内部通过解引用操作直接修改了主函数中x和y的内存数据,从而实现真正的交换。

五、指针使用的注意事项

1.避免野指针:初始化是关键

野指针指的是指向不确定内存地址的指针(未初始化或指向的内存已被释放)。访问野指针会导致不可预测的后果(例如修改系统关键内存、程序崩溃)。

如何避免?

定义时立即初始化:要么指向一个已存在的变量(如 
int* p = &num;
),要么初始化为 
nullptr
(C++11 引入的空指针常量,替代旧版的 
NULL
)。指针指向的内存被释放后,及时置空:例如动态内存释放后,需将指针设为 
nullptr
,避免后续误用。



int* p; // 危险!未初始化的野指针,指向随机地址
*p = 10; // 操作野指针,程序可能崩溃
 
// 正确做法
int* p1 = nullptr; // 初始化为空指针
int num = 0;
int* p2 = &num; // 明确指向一个变量
2. 禁止对空指针解引用


nullptr
 表示指针不指向任何有效内存,对其解引用(
*nullptr
)会直接导致程序崩溃。因此使用指针前,务必检查其是否为 
nullptr



int* p = nullptr;
if (p != nullptr) { // 使用前先判断
    *p = 10; // 只有指针有效时才操作
} else {
    cout << "指针为空,无法操作" << endl;
}
3. 避免指针越界访问

指针可以通过加减运算移动(称为 “指针偏移”),但需严格控制范围,禁止访问超出数组或动态内存的边界,否则会破坏其他内存的数据。



int arr[3] = {1, 2, 3};
int* p = arr; // 指针指向数组首元素
 
// 合法访问:指向数组内的元素
cout << *(p + 0) << endl; // 1(arr[0])
cout << *(p + 1) << endl; // 2(arr[1])
 
// 越界访问:超出数组范围,危险!
cout << *(p + 3) << endl; // 访问未知内存,结果随机
4. 动态内存管理:配对使用 
new
 与 
delete

指针常用来管理动态内存(程序运行时手动分配的内存),使用 
new
 分配内存后,必须用 
delete
 释放(数组用 
delete[]
),否则会导致内存泄漏(内存被占用却无法回收,长期运行会耗尽内存)。



// 动态分配单个int变量
int* p = new int; // 分配内存,p指向该内存
*p = 100;
delete p; // 释放内存
p = nullptr; // 释放后及时置空,避免野指针
 
// 动态分配数组
int* arr = new int[5]; // 分配5个int的数组
arr[0] = 1;
delete[] arr; // 释放数组内存(必须用delete[])
arr = nullptr;

注意

不可对同一块内存多次 
delete
(会导致内存损坏)。避免 “悬垂指针”:内存被 
delete
 后,指针仍指向原地址,需及时置为 
nullptr

5. 指针类型必须与指向的数据类型匹配

指针的类型决定了它如何解析内存中的数据(例如 
int*
 每次访问 4 字节,
char*
 每次访问 1 字节)。若类型不匹配,会导致数据解析错误。



int num = 0x12345678; // 假设int占4字节
char* p = (char*)&num; // 强制类型转换(不推荐)
 
// char* 每次访问1字节,解析结果与int*不同
cout << hex << (int)*p << endl; // 输出78(仅低1字节)

6.

6. 警惕指针的生命周期

指针的生命周期需与指向的变量 / 内存一致。例如,函数内的指针若指向局部变量,函数结束后局部变量被销毁,指针会变成野指针。



int* getPtr() {
    int x = 10;
    return &x; // 危险!返回局部变量的地址
}
 
int main() {
    int* p = getPtr(); // p指向已销毁的内存(野指针)
    *p = 20; // 操作无效内存,程序可能崩溃
    return 0;
}

六、总结

指针是 C++ 中 “双刃剑”—— 它赋予程序直接操作内存的能力,提升效率的同时也带来了风险。掌握指针的核心是理解 “地址” 与 “解引用” 的逻辑,而安全使用的关键在于:初始化指针、检查空指针、控制访问范围、正确管理动态内存。只有严格遵循这些规则,才能充分发挥指针的优势,避免内存错误。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...