什么是指针?

什么是指针?整体性把握一下!

定义

指针(Pointer)是一种特殊的变量,它存储的不是普通的数据值(如 5, ‘A’, 3.14),而是另一个变量在内存中的地址

为什么需要指针?

指针的出现解决了编程中的许多难题:

  1. 高效的函数参数传递:当需要向函数传递一个大型结构体或对象时,直接传递整个数据会产生巨大的开销(需要完整复制一份)。而传递指针(通常只有 4 或 8 个字节),函数可以直接在原始数据上进行修改,效率极高。
  2. 实现动态内存管理:在程序运行时,我们需要动态地创建和销毁变量(例如,创建一个大小不固定的数组)。这只能通过 new (或 malloc) 在堆上分配内存,并通过指针来访问这块新内存。
  3. 处理复杂数据结构:链表、树、图等高级数据结构的核心就是节点之间的“链接”,这种链接关系正是通过指针来实现的。
  4. 直接操作内存:在某些底层编程场景(如操作系统、嵌入式开发),需要直接操作硬件内存地址,指针是实现这一功能的唯一途径。

指针的基本语法

1. 声明一个指针

声明指针需要使用 *(星号)符号。

// 语法: 数据类型 *指针名;
int *p_num; // p_num 是一个指向 int 类型数据的指针
double *p_price; // p_price 是一个指向 double 类型数据的指针
char *p_char; // p_char 是一个指向 char 类型数据的指针
2. 获取地址(& 运算符)

如果我们想得到一个变量的地址,需要使用取地址运算符 &

int age = 25;
int *p_age = &age; // 将 age 变量的地址赋值给 p_age 指针

现在,p_age 中存储的就是 age 变量的内存地址。

3. 解引用(* 运算符)

解引用就是通过指针,访问它所指向地址的值。

int age = 25;
int *p_age = &age;
cout << "通过变量访问 age: " << age << endl; // 输出 25
cout << "通过指针访问 age: " << *p_age << endl; // 输出 25

*p_age 的意思就是:“找到 p_age 中存储的地址,然后去该地址读取值”。

4. 修改指针指向的值

既然可以访问,当然也可以修改。

int score = 88;
int *p_score = &score;
cout << "修改前 score: " << score << endl; // 输出 88
*p_score = 95; // 通过指针修改 score 的值
cout << "修改后 score: " << score << endl; // 输出 95

指针的运算

指针可以进行有限的算术运算,最常见的是指针的递增和递减
关键规则:指针的移动单位不是固定的字节,而是它所指向的数据类型的大小

int arr[5] = {10, 20, 30, 40, 50};
int *p_arr = arr; // 数组名 arr 会“衰变”为其首元素的地址
cout << "p_arr 指向的值: " << *p_arr << endl; // 输出 10
p_arr++; // p_arr 向后移动一个 int 的距离(通常是 4 字节)
cout << "p_arr++ 后指向的值: " << *p_arr << endl; // 输出 20
p_arr--; // p_arr 向前移动一个 int 的距离
cout << "p_arr-- 后指向的值: " << *p_arr << endl; // 输出 10
  • p_arr++ 实际上执行的是 p_arr = p_arr + sizeof(int)
  • 指针的加减运算在遍历数组时非常有用。

指针的特殊类型

1. 空指针 (nullptr / NULL)

空指针是一个不指向任何有效内存地址的指针。它是一个特殊的值,用于表示“没有指向任何东西”。
在 C++11 及以后版本,推荐使用 nullptr

int *p_null = nullptr; // 推荐
// int *p_null = NULL; // C 风格,也可以
// 在使用前必须检查指针是否为空
if (p_null != nullptr) {
    cout << *p_null << endl; // 不会执行到这里
} else {
    cout << "p_null 是一个空指针,不能解引用!" << endl;
}

解引用空指针会导致程序崩溃(段错误 Segment Fault),所以这是一个必须遵守的规则。

2. 指针和数组的关系

在 C/C++ 中,数组名和指向数组首元素的指针在很多情况下可以互换使用

int numbers[3] = {1, 2, 3};
int *p = numbers; // p 指向 numbers[0]
// 使用指针访问数组元素
cout << p[0] << endl; // 输出 1
cout << p[1] << endl; // 输出 2
cout << *(p + 2) << endl; // 输出 3
// 数组名也可以看作一个常量指针
// numbers++; // 错误!数组名是常量指针,不能修改
3. 指针和函数
a) 指针作为函数参数

这是指针最常见的用途之一,实现“传地址调用”。

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main() {
    int x = 10, y = 20;
    cout << "交换前: x = " << x << ", y = " << y << endl;
    swap(&x, &y); // 传递 x 和 y 的地址
    cout << "交换后: x = " << x << ", y = " << y << endl;
    return 0;
}

如果这里传递的是 xy 的值(swap(x, y)),函数内的交换不会影响外部的 xy

b) 函数指针

函数指针是一个指向函数的指针,它存储的是函数的入口地址。

// 定义一个函数
int add(int a, int b) {
    return a + b;
}
int main() {
    // 声明一个函数指针 p_func
    // 它指向一个返回 int, 参数为 (int, int) 的函数
    int (*p_func)(int, int);
    // 将 add 函数的地址赋给 p_func
    p_func = add;
    // 通过函数指针调用函数
    int result = p_func(5, 3);
    cout << "结果: " << result << endl; // 输出 8
    return 0;
}

函数指针在回调、实现多态等高级编程中非常有用。

指针的风险与最佳实践

指针功能强大,但也非常危险。不当使用会导致严重问题。

  1. 野指针

    • 定义:一个指向“无效内存区域”的指针。它可能未被初始化,或者指向的内存已经被释放。
    • 危害:解引用野指针会导致程序崩溃或不可预测的行为。
    • 预防永远在定义指针时立即初始化。如果暂时没有指向的对象,就初始化为 nullptr
    int *p_bad; // 危险!野指针
    int *p_safe = nullptr; // 安全
    
  2. 内存泄漏

    • 定义:当使用 new (或 malloc) 在堆上分配了内存后,如果没有使用 delete (或 free) 释放它,并且指向这块内存的指针丢失了,这块内存就再也无法被访问和回收,造成了泄漏。
    • 危害:长期运行的程序会因内存泄漏而耗尽所有可用内存,最终崩溃。
    • 预防对于 new 出来的内存,必须有对应的 delete。在现代 C++ 中,推荐使用智能指针(std::unique_ptr, std::shared_ptr)来自动管理内存,避免忘记 delete
  3. 重复释放

    • 定义:对同一块内存调用两次 delete
    • 危害:导致未定义行为,通常程序会崩溃。
    • 预防:确保每个 new 只对应一个 delete

现代 C++ 的替代方案(智能指针)

为了解决原始指针带来的内存管理难题,C++11 引入了智能指针。它们是封装了原始指针的类对象,能够在其生命周期结束时自动释放所指向的内存,极大地提高了代码的安全性和可维护性。

  • std::unique_ptr:独占所有权的指针。一个对象只能被一个 unique_ptr 拥有。
  • std::shared_ptr:共享所有权的指针。多个 shared_ptr 可以指向同一个对象,它会自动记录引用计数,当最后一个 shared_ptr 被销毁时,内存才会被释放。
    示例:
#include <iostream>
#include <memory> // 包含智能指针头文件
void useSmartPointer() {
    // 使用 unique_ptr 自动管理内存
    std::unique_ptr<int> p_unique = std::make_unique<int>(100);
    std::cout << "unique_ptr 的值: " << *p_unique << std::endl;
    // p_unique 的生命周期结束时,delete 会被自动调用
} // p_unique 在这里被销毁,内存自动释放
int main() {
    useSmartPointer();
    std::cout << "函数执行完毕,内存已自动释放。" << std::endl;
    return 0;
}

总结表格

特性描述
核心概念指针是存储内存地址的变量。
& 运算符取地址,用于获取变量的内存地址。
* 运算符解引用,用于访问指针指向地址的值。
主要用途高效参数传递、动态内存管理、构建复杂数据结构。
关键类型空指针 (nullptr)、函数指针。
主要风险野指针、内存泄漏、重复释放。
现代方案智能指针 (std::unique_ptr, std::shared_ptr) 用于自动内存管理。

(PS:面试问到了)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九层指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值