当前位置: 首页 > news >正文

C++指针:高效编程的核心钥匙

目录

​编辑

1. 什么是指针?

2. 为什么使用指针?

3. 基本语法和操作

a. 声明指针

b. 取地址操作符 (&)

c. 解引用操作符 (*)

d. nullptr (空指针)

4. 指针与数组

5. 动态内存分配 (new 和 delete)

a. 使用 new 分配内存

b. 使用 delete 释放内存

6. 指针与函数

a. 按引用传递(模拟)

b. 传递数组

7. 指针的指针 (**)

8. const 与指针

总结与最佳实践


1. 什么是指针?

指针是一个变量,其值是另一个变量的内存地址

你可以把它想象成一张存储了某个房子具体地址的纸条,而不是房子本身。通过这个地址(指针),你可以找到并操作那所房子(内存中的数据)。

2. 为什么使用指针?

  • 直接操作内存:实现高效的程序,尤其是在资源受限的系统中。

  • 实现“按引用传递”:允许函数修改调用者提供的变量,而不是拷贝一份副本。

  • 动态内存分配:在程序运行时(而不是编译时)申请和释放内存,用于构建灵活的数据结构(如链表、树等)。

  • 实现多态:通过基类指针管理派生类对象,这是面向对象编程的核心。

  • 高效地操作数组和字符串

  • 在函数间传递大型结构:传递一个指针(一个地址)比传递整个结构体拷贝要快得多。


3. 基本语法和操作

a. 声明指针

指针在声明时使用 * 符号,并指定它指向的数据类型。

cpp

int* ptr;    // 声明一个指向整型的指针 ptr
double* dPtr; // 声明一个指向双精度浮点型的指针 dPtr
char* cPtr;  // 声明一个指向字符型的指针 cPtr

* 的位置可以紧挨类型,也可以紧挨变量名,甚至可以放在中间。但为了清晰,建议紧挨类型。

int* p1; // 清晰:p1 是一个指向 int 的指针
int * p2; // 也正确,但稍显混乱
int *p3; // 也正确,强调 *p3 是一个 int
b. 取地址操作符 (&)

这个操作符用于获取一个变量的内存地址。

int var = 42;
int* ptr = &var; // 将变量 var 的地址赋值给指针 ptrcout << "var 的值: " << var << endl;   // 输出: 42
cout << "var 的地址: " << &var << endl; // 输出: 0x7ffd2d43eabc (一个十六进制内存地址)
cout << "ptr 的值: " << ptr << endl;    // 输出: 0x7ffd2d43eabc (和 &var 相同)
c. 解引用操作符 (*)

这个操作符用于获取或修改指针所指向地址中存储的值。

int var = 42;
int* ptr = &var;cout << *ptr << endl; // 输出: 42 (通过指针获取 var 的值)*ptr = 100; // 通过指针修改 var 的值
cout << var << endl; // 输出: 100 (var 的值被成功修改)

关键区别

  • 声明中,* 表示这是一个指针变量 (int* ptr;)。

  • 表达式中,* 是解引用操作符,用于访问指针指向的值 (*ptr = 100;)。

d. nullptr (空指针)

这是一个表示“指针不指向任何有效内存地址”的特殊值。在 C++11 及之后的标准中,应使用 nullptr 来代替旧的 NULL0

总是初始化指针! 未初始化的指针指向随机的内存地址,访问它会导致程序崩溃或不可预知的行为。

int* ptr = nullptr; // 好的做法:初始化为空指针if (ptr != nullptr) {cout << *ptr; // 安全地检查后再使用
} else {cout << "指针为空!";
}

4. 指针与数组

数组名本质上就是一个指向数组第一个元素的常量指针。

int numbers[5] = {10, 20, 30, 40, 50};
int* ptr = numbers; // 等价于 int* ptr = &numbers[0];cout << *ptr << endl;    // 输出: 10 (第一个元素)
cout << *(ptr + 1) << endl; // 输出: 20 (第二个元素)
// 注意:ptr + 1 会自动移动到下一个 int 的地址(通常是当前地址 + 4 字节)// 使用指针遍历数组
for (int i = 0; i < 5; i++) {cout << *(ptr + i) << " "; // 输出: 10 20 30 40 50
}
// 或者更常见的写法:ptr[i] 等价于 *(ptr + i)

5. 动态内存分配 (newdelete)

这是指针最重要的用途之一。它允许你在堆(Heap) 上申请内存,这块内存的生命周期由你手动控制。

a. 使用 new 分配内存

// 分配一个 int 大小的内存,并将地址赋给 ptr
int* ptr = new int; 
*ptr = 42; // 在动态分配的内存中存储值// 分配一个包含 5 个 int 的数组
int* arrayPtr = new int[5]; 
for (int i = 0; i < 5; i++) {arrayPtr[i] = i * 10; // 初始化数组
}
b. 使用 delete 释放内存

至关重要: 每一个 new 都必须对应一个 delete,否则会导致内存泄漏

// 释放单个元素的内存
delete ptr; 
ptr = nullptr; // 好习惯:释放后立即将指针置空,防止“悬空指针”// 释放数组的内存
delete[] arrayPtr; // 注意:释放数组要用 delete[]
arrayPtr = nullptr;

6. 指针与函数

a. 按引用传递(模拟)

通过指针,函数可以修改外部变量的值。

void increment(int* value) {(*value)++; // 解引用并增加其值
}int main() {int a = 10;increment(&a); // 传递变量 a 的地址cout << a; // 输出: 11return 0;
}
b. 传递数组

函数接收数组时,实际上接收的是指向数组首元素的指针。

void printArray(int* arr, int size) {for (int i = 0; i < size; i++) {cout << arr[i] << " ";}
}int main() {int myArray[3] = {1, 2, 3};printArray(myArray, 3); // 输出: 1 2 3return 0;
}

7. 指针的指针 (**)

指针本身也是变量,它也有自己的内存地址。指向指针的指针称为二级指针。

int var = 42;
int* ptr = &var;
int** pptr = &ptr; // pptr 是一个指向指针 ptr 的指针cout << **pptr << endl; // 输出: 42
// *pptr 得到 ptr 的值(即 var 的地址)
// **pptr 得到 var 的值

8. const 与指针

const 和指针的组合容易混淆,主要看 const 修饰的是什么。

  1. 指向常量的指针:指针指向的值不能被修改。

const int* ptr = &var;
// *ptr = 100; // 错误!不能通过 ptr 修改 var 的值
// var = 100;   // 正确,var 本身不是常量,可以直接修改

指针常量:指针本身(存储的地址)不能被修改,不能再指向别处。

int* const ptr = &var;
*ptr = 100; // 正确,可以修改指向的值
// ptr = &anotherVar; // 错误!ptr 不能再指向其他地址

指向常量的指针常量:指针指向的值和指针本身的地址都不能修改。

const int* const ptr = &var;
// *ptr = 100; // 错误!
// ptr = &anotherVar; // 错误!

总结与最佳实践

操作语法说明
声明指针type* name;声明一个指向 type 类型的指针
取地址&variable获取 variable 的内存地址
解引用*pointer获取 pointer 指向地址的值
动态分配new type在堆上分配内存,返回地址
动态释放delete pointer释放 new 分配的内存
空指针nullptr表示指针不指向任何有效对象
  • 始终初始化指针:声明时立即初始化为 nullptr 或有效地址。

  • newdelete 必须成对出现:防止内存泄漏。

  • 释放后置空delete ptr; ptr = nullptr; 防止悬空指针。

  • 谨慎使用:指针功能强大,但也容易引入复杂性和错误(如内存泄漏、悬空指针、野指针)。在现代 C++ 中,应优先考虑使用智能指针(std::unique_ptr, std::shared_ptr引用来管理资源,它们可以自动处理内存释放,大大减少错误。

希望这个详细的解释能帮助你彻底理解 C++ 指针!

http://www.dtcms.com/a/393256.html

相关文章:

  • WIN11操作系统安装PL2303TA USB转串口驱动问题
  • ​​[硬件电路-280]:两相步进电机的功能、四个接口信号与工作原理详解(电能转化为机械能)
  • conda换源
  • 博客系统UI自动化测试报告
  • 大语言模型 LLM 通过 Excel 知识库 增强日志分析,根因分析能力的技术方案(6):vLLM 为什么能够成为企业级推理事实上的标准?
  • Redis最佳实践——秒杀系统设计详解
  • 数字孪生能做什么?
  • 每天学习一个统计检验方法--协方差分析 (ANCOVA)(以噩梦障碍中的心跳诱发电位研究为例)
  • 2025年CSP-J初赛真题及答案解析
  • OpenHarmony电量与LED灯颜色定制开发
  • OpenHarmony 显示Display驱动全栈解析:DisplayLayer + Gralloc + Gfx 三位一体,打造高性能图形底座
  • 诊断中的一些复位跳转
  • Python爬虫实战:临近双节,构建携程网最新特价机票数据采集与推荐系统
  • 容器主机名设置在云服务器多容器环境的配置流程
  • UE5 socket通信
  • 如何用kimi写一个最小pdf查看软件
  • DTS和PTS
  • 【开题答辩实录分享】以《“平安行”驾校信息管理系统的设计与实现》为例进行答辩实录分享
  • Modbus RTU/TCP转EtherNet/IP网关配置:西门子PLC控制伦茨变频器
  • GEO完全指南:AI时代内容优化的新范式
  • 02-安装DRF框架
  • 浅谈矩阵在机器学习线性回归算法中的数学推导
  • Linux 系统编程中的Redis
  • 【OpenGL】绘制彩色立方体
  • 21.继承与混入
  • Python 开发!ImprovePdf 用算法提升PDF清晰度,免费开源工具
  • P1879 [USACO06NOV] Corn Fields G-提高+/省选-
  • 二分答案:跳石头
  • 注解学习,简单测试框架
  • python 自定义装饰器 + 框架