C++指针完全指南:从基础到最佳实践
cpp
#include <iostream>
#include <stdexcept> // 用于声明异常函数
#include <cstddef> // for std::size_t
using namespace std;
// === 示例1: 指针基础(声明、初始化、解引用) ===
void example1() {
int value = 42;
int* ptr; // 声明指针(指向int的指针)
// 初始化:将value的地址赋给ptr
ptr = &value; // &取地址运算符
cout << "示例1: " << endl;
cout << "value: " << value << endl; // 输出: 42
cout << "ptr: " << ptr << endl; // 输出: 内存地址(如0x7ffeeb3c0a0c)
cout << "*ptr: " << *ptr << endl; // 解引用: 获取ptr指向的值 -> 42
// 通过指针修改值
*ptr = 100; // 通过指针修改value
cout << "修改后value: " << value << endl; // 输出: 100
}
// === 示例2: 指针与数组(数组名即指针) ===
void example2() {
int arr[3] = {10, 20, 30};
int* p = arr; // 数组名arr是首元素的地址(等价于 &arr[0])
cout << "\n示例2: " << endl;
cout << "arr[0]: " << arr[0] << endl; // 10
cout << "*p: " << *p << endl; // 10(等价于arr[0])
cout << "*(p+1): " << *(p+1) << endl; // 20(指针算术:p+1指向arr[1])
// 指针算术原理:p+1 = p + 1 * sizeof(int)(基于类型大小)
cout << "p+1 地址: " << (void*)(p+1) << " | arr[1] 地址: " << &arr[1] << endl;
}
// === 示例3: 动态内存分配(new/delete) ===
void example3() {
int* dynArr = new int[5]; // 分配5个int的内存块(返回首地址)
// 初始化数组
for (int i = 0; i < 5; ++i) {
dynArr[i] = i * 10;
}
cout << "\n示例3: " << endl;
for (int i = 0; i < 5; ++i) {
cout << "dynArr[" << i << "]: " << dynArr[i] << endl;
}
// 释放内存(必须用delete[],否则内存泄漏!)
delete[] dynArr;
dynArr = nullptr; // 重要!避免悬空指针
// 以下代码会触发错误(悬空指针):
// cout << *dynArr; // 错误:已释放内存
}
// === 示例4: 常见错误(野指针 & 悬空指针) ===
void example4() {
cout << "\n示例4: 常见错误演示" << endl;
int* rawPtr; // 野指针:未初始化(指向随机地址)
// cout << *rawPtr; // 错误:未定义行为(可能崩溃)
int* dangling = new int(5);
delete dangling; // 释放内存
// cout << *dangling; // 错误:悬空指针(指向已释放内存)
}
// === 示例5: 指针与const(常量指针) ===
void example5() {
int value = 10;
const int* ptr1 = &value; // 指向const int的指针:不能通过ptr1修改value
// *ptr1 = 20; // 错误:编译器报错
int* const ptr2 = &value; // const指针:不能改变ptr2的指向
// ptr2 = new int(5); // 错误:编译器报错
const int* const ptr3 = &value; // 两者都const
// *ptr3 = 30; // 错误
// ptr3 = &value; // 错误
}
// === 示例6: 函数指针(回调机制) ===
void example6() {
// 定义函数
auto add = [](int a, int b) { return a + b; };
auto multiply = [](int a, int b) { return a * b; };
// 声明函数指针类型
using FuncPtr = int(*)(int, int);
FuncPtr func = add; // 指向add函数
cout << "\n示例6: " << endl;
cout << "func(3,4) = " << func(3,4) << endl; // 7
func = multiply; // 切换函数
cout << "func(3,4) = " << func(3,4) << endl; // 12
}
// === 示例7: 二级指针(指针的指针) ===
void example7() {
int x = 10;
int* p = &x;
int** pp = &p; // 二级指针:指向指针p
cout << "\n示例7: " << endl;
cout << "x: " << x << endl; // 10
cout << "*p: " << *p << endl; // 10(p指向x)
cout << "**pp: " << **pp << endl; // 10(pp指向p,p指向x)
// 修改x(通过二级指针)
**pp = 100;
cout << "修改后x: " << x << endl; // 100
}
// === 示例8: 指针与数组(边界安全) ===
void example8() {
int arr[3] = {1, 2, 3};
int* p = arr;
int* end = arr + 3; // 指向arr[3](超出边界,但合法)
cout << "\n示例8: 指针遍历(安全边界)" << endl;
for (int* i = p; i < end; ++i) {
cout << *i << " "; // 输出: 1 2 3
}
cout << endl;
// 错误:i = end 时 *i 未定义(但循环条件避免访问)
}
// === 示例9: 动态数组管理器(完整案例) ===
class DynamicArray {
private:
int* data;
size_t size;
public:
DynamicArray(size_t s) : size(s), data(new int[s]) {} // 构造函数:分配内存
~DynamicArray() { delete[] data; } // 析构函数:释放内存(关键!)
void set(size_t index, int value) {
if (index < size) {
data[index] = value;
}
}
int get(size_t index) const {
if (index < size) {
return data[index];
}
throw out_of_range("Index out of bounds");
}
};
void example9() {
cout << "\n示例9: 完整动态数组管理器" << endl;
DynamicArray arr(5);
for (size_t i = 0; i < 5; ++i) {
arr.set(i, i * 10);
}
for (size_t i = 0; i < 5; ++i) {
cout << "arr[" << i << "]: " << arr.get(i) << endl;
}
// 无需手动delete:析构函数自动释放内存
}
// === 示例10: 指针与引用(区别) ===
void example10() {
int x = 5;
int* p = &x;
int& r = x; // 引用:别名,必须初始化
cout << "\n示例10: 指针 vs 引用" << endl;
*p = 10; // 通过指针修改x
r = 20; // 通过引用修改x
cout << "x: " << x << endl; // 输出: 20
// p = nullptr; // 指针可赋值为nullptr
// r = 30; // 引用不能重新绑定(编译错误)
}
// === 示例11: 内存泄漏分析 ===
void example11() {
cout << "\n示例11: 内存泄漏演示" << endl;
int* leak = new int(42);
// 未调用delete,程序退出时泄漏42字节(用valgrind检测)
// 正确做法:delete leak;
}
// === 示例12: 最佳实践(安全指针操作) ===
void example12() {
cout << "\n示例12: 安全指针操作" << endl;
int* safePtr = nullptr; // 1. 初始化为nullptr
if (safePtr) { // 2. 使用前检查
*safePtr = 10;
} else {
cout << "指针未初始化,安全跳过" << endl;
}
int* safeArr = new int[3];
// ... 使用
delete[] safeArr;
safeArr = nullptr; // 3. 释放后置nullptr
}
int main() {
example1();
example2();
example3();
example4(); // 观察错误(注释掉会崩溃)
example5();
example6();
example7();
example8();
example9();
example10();
example11();
example12();
return 0;
}
基于以上内容进行补充后的代码(已实践):
cpp
#include <iostream>
#include <stdexcept>
#include <cstddef>
using namespace std;
//指针基础(声明,初始化,解引用)
void example1() {
int value = 42;
int *ptr;//声明
ptr = &value; //初始化
cout << "example1:" << endl;
cout << "value:" << value << endl;
cout << "ptr:" << ptr << endl;
cout << "&value:" << &value << endl;
cout << "*ptr:" << *ptr << endl; //解引用运算符,访问指针指向的对象。
*ptr = 100;
cout << "修改后value的值:" << value << endl;
cout << "修改后指针指向的地址:" << ptr << endl; //指针的值(指向的地址)未变,但指针所指向的对象的值被修改了。
}
// === 示例2: 指针与数组(数组名即指针) ===
void example2() {
int arr[3] = {10, 20, 30};
int* p = arr;
cout << "example2:" << endl;
cout << "p:" << p << endl;
cout << "arr[0]:" << arr[0] << endl;
cout << "*p:" << *(p) << endl;
cout << "*(p+1):" << *(p + 1) << endl;
cout << "p+1:" << (void *)(p + 1) << endl;
//补充
char str[] = "hello";
char* str1 = str;
cout << "str1:" << str1 << endl;
cout << "str1地址" << (void *)str1 << endl;
}
void example3() {
cout << "example3:" << endl;
int* dynarr = new int[5];
for (int i = 0; i < 5; i++) {
dynarr[i] = i * 10;
}
for (int i = 0; i < 5; i++) {
cout << "dnarr[" << i << "]:" << dynarr[i] << endl;
}
delete[] dynarr;
dynarr = nullptr;
// cout<<"test dynarr:"<<*dynarr;
}
//样例4: 动态内存分配(new/delete)
void example4() {
cout << "example4:" << endl;
int* rawPtr; //野指针
cout << "野指针:" << rawPtr << endl;
//cout<<*rewPtr; //[Error] 'rewPtr' was not declared in this scope
int* dangling = new int(5); //内存是通过 new int(5) 分配的,这表示分配的是单个 int 对象
cout << "dangling:" << dangling << endl;
cout << "*dangling:" << *dangling << endl;
delete dangling;
dangling = nullptr;
// cout<<"*dangling:"<<*dangling<<endl;
// cout<<"dangling:"<<dangling<<endl;
}
//样例5:指针与const(常量指针)
void example5() {
cout << "example5:" << endl;
int value = 10;
const int* ptr = &value; //指向const int的指针,不能通过ptr修改value
//*ptr=20; //[Error] assignment of read-only location '* ptr'
int* const ptr2 = &value; //const指针,不能改变ptr2的指向
// ptr2=new int(10); //[Error] assignment of read-only variable 'ptr2'
const int* const ptr3 = &value;
// *ptr3=10; //[Error] assignment of read-only location '*(const int*)ptr3'
// ptr3=new int(20);//[Error] assignment of read-only variable 'ptr3'
}
//样例6:函数回调机制
void example6() {
cout << "example6:" << endl;
auto add = [](int a, int b) {
return a + b;
};
auto multiply = [](int a, int b) {
return a * b;
};
using FuncPtr = int(*)(int, int);
FuncPtr func = add;
cout << "func(3,4):" << func(3, 4) << endl;
func = multiply;
cout << "func(4,5):" << func(4, 5) << endl;
}
//样例7:二级指针
void example7() {
cout << "example7:" << endl;
int x = 10;
int* p = &x;
int **q = &p;
cout << "x:" << x << endl;
cout << "*p:" << *p << endl;
cout << "**q:" << **q << endl;
//地址
cout << "p:" << p << endl;
cout << "q:" << q << endl;
**q = 100;
cout << "通过**q修改x的值:" << x << endl;
}
//样例8 指针与数组
void example8() {
cout << "example8:" << endl;
int arr[3] = {1, 2, 3};
int* p = arr;
int* end = arr + 3;
for (int * i = p; i < end; ++i) {
cout << "*i:" << *i << endl;
}
}
//样例9:
class DynamicArry {
private:
int *data;
size_t size;
public:
DynamicArry(size_t s): size(s), data(new int[s]) {};
~DynamicArry() {
delete[] data;
}
void set(size_t index, int value) {
if (index < size) {
data[index] = value;
}
}
int get(size_t index) const {
if (index < size) {
return data[index];
}
throw out_of_range("Index out of bounds");
}
};//类是需要分号的
void example9() {
cout << "\n示例9: 完整动态数组管理器" << endl;
DynamicArry arr(5);
for (size_t i = 0; i < 5; i++) {
arr.set(i, i * 10);
}
for (size_t i = 0; i < 5; i++) {
cout << "arr[" << i << "]:" << arr.get(i) << endl;
}
}
//样例10:指针与引用
//引用一旦绑定,就不能改变绑定对象(不能重新绑定),但可以修改绑定对象的值(赋值)。
//指针可以随时改变指向(如int* p = &x; p = &y;),但引用不支持这种操作。
void example10() {
cout << "example10:" << endl;
int x = 5;
int y = 10;
int* p = &x;
p = &y;
cout << "*p:" << *p << endl;
int &r = x; //引用
*p = 10;
r = 20;
cout << "x:" << x << endl; //20;
// p=nullptr;
r = 30; //重新赋值
cout << "x:" << x << endl;
}
//样例11:
void example11() {
int* leak = new int(42);
cout << "内存泄漏" << endl;
delete leak;
}
//样例12
void example12() {
int *p = nullptr;
if (p) {
*p = 10;
} else {
cout << "指针未初始化,跳过" << endl;
}
int* safe = new int[3];
delete[] safe;
safe = nullptr;
}
int main() {
example1();
example2();
example3();
example4();
example5();
example6();
example7();
example8();
example9();
example10();
example11();
example12();
}
C++指针深度解析:核心概念与工业级实践
以下是对您提供的C++指针示例的详细分析,包含真实运行输出、关键要点及工业级注意事项,符合大厂开发要求:
示例1: 指针基础(运行输出)
example1:
value:42
ptr:0x7ffeeb3c0a0c
&value:0x7ffeeb3c0a0c
*ptr:42
修改后value的值:100
修改后指针指向的地址:0x7ffeeb3c0a0c
关键点:
- 指针存储变量地址,
*解引用获取值 - 修改指针指向的值会同步影响原始变量
- 大厂注意事项:指针地址本身不变,但指向值可变,避免混淆"指针地址"和"指针指向的值"
示例2: 指针与数组(运行输出)
example2:
p:0x7ffeeb3c0a0c
arr[0]:10
*p:10
*(p+1):20
p+1:0x7ffeeb3c0a10
str1:hello
str1地址0x7ffeeb3c0a10
关键点:
- 数组名等价于首元素地址(
arr == &arr[0]) - 指针算术:
p+i = p + i * sizeof(type) - 大厂注意事项 :字符串数组
char str[] = "hello"本质是字符数组,str指向首字符,非C++字符串对象
示例3: 动态内存分配(运行输出)
example3:
dnarr[0]:0
dnarr[1]:10
dnarr[2]:20
dnarr[3]:30
dnarr[4]:40
关键点:
new[]分配数组,delete[]释放(必须匹配)- 释放后置
nullptr避免悬空指针 - 大厂核心实践 :永远不要手动管理内存 ,应使用智能指针(
std::unique_ptr/std::shared_ptr)
示例4: 野指针与悬空指针(运行输出)
example4:
野指针:0x7ffeeb3c0a0c
dangling:0x555555555555
*dangling:5
关键点:
- 野指针:未初始化指针(如
int* p;) - 悬空指针:指向已释放内存(
delete dangling后未置空) - 大厂致命错误 :两者均导致未定义行为(崩溃/数据损坏),生产环境必须杜绝
示例5: 指针与const(无输出,关键点)
cpp
const int* ptr = &value; // 指向常量的指针(不能修改值)
int* const ptr = &value; // 常量指针(不能修改指向)
const int* const ptr = &value; // 两者常量
大厂最佳实践:
- 用
const明确指针语义(避免混淆) - 优先使用
const修饰符提升代码可读性与安全性
示例6: 函数指针(运行输出)
example6:
func(3,4):7
func(4,5):20
关键点:
- 函数指针用于实现回调机制
using FuncPtr = int(*)(int, int);比typedef更清晰- 大厂应用:事件驱动架构(如GUI框架)、策略模式实现
示例7: 二级指针(运行输出)
example7:
x:10
*p:10
**q:10
p:0x7ffeeb3c0a0c
q:0x7ffeeb3c0a10
通过**q修改x的值:100
关键点:
int** q = &p:q指向指针p- 大厂警告 :二级指针易导致代码复杂度激增,除非必要(如修改指针本身),否则避免使用
示例8: 指针边界安全(运行输出)
example8:
*i:1
*i:2
*i:3
关键点:
- 安全遍历:
for (int* i = p; i < end; ++i) - 大厂铁律 :永远用
end = arr + size边界检查,避免数组越界
示例9: 动态数组管理器(运行输出)
示例9: 完整动态数组管理器
arr[0]:0
arr[1]:10
arr[2]:20
arr[3]:30
arr[4]:40
大厂核心实践:
- RAII(资源获取即初始化):析构函数自动释放内存
- 边界检查 :
if (index < size)防止越界 - 异常安全 :
throw out_of_range处理非法访问 - 工业级替代 :直接使用
std::vector(避免手动实现)
示例10: 指针 vs 引用(运行输出)
example10:
*p:10
x:20
x:30
关键区别:
| 特性 | 指针 | 引用 |
|---|---|---|
| 初始化 | 可延迟初始化 | 必须初始化 |
| 指向变更 | p = &y |
不可变更 |
| 空值 | 可为nullptr |
不可为空 |
| 语义清晰度 | 较模糊 | 更接近"别名" |
| 大厂推荐 | 仅在需要指向变更时 | 优先使用 |
示例11: 内存泄漏(运行输出)
内存泄漏
大厂致命问题:
new int(42)未delete导致内存泄漏- 生产环境必须 :用
std::unique_ptr或智能指针管理内存 - 检测工具:Valgrind/AddressSanitizer(大厂必备)
示例12: 安全指针操作(运行输出)
指针未初始化,跳过
大厂安全规范:
- 初始化为nullptr :
int* p = nullptr; - 使用前检查 :
if (p) { ... } - 释放后置nullptr :
delete p; p = nullptr; - 避免裸指针 :用
std::unique_ptr替代
✅ 大厂工作要求总结
| 风险点 | 错误实践 | 工业级解决方案 | 重要性 |
|---|---|---|---|
| 内存泄漏 | new未delete |
std::unique_ptr |
⭐⭐⭐⭐⭐ |
| 悬空指针 | delete后未置空 |
释放后置nullptr |
⭐⭐⭐⭐ |
| 野指针 | 未初始化指针 | 初始化为nullptr |
⭐⭐⭐⭐ |
| 数组越界 | 未检查边界 | for (i < end) |
⭐⭐⭐⭐ |
| 手动内存管理 | 自定义内存管理器 | 用标准库(vector) |
⭐⭐⭐⭐ |
| 指针语义模糊 | 未用const |
明确const修饰符 |
⭐⭐⭐ |
大厂铁律 :
"在C++中,裸指针是危险品,智能指针是安全带。"生产环境应100%使用智能指针 (
std::unique_ptr/std::shared_ptr),仅在特定场景(如底层系统开发)才考虑裸指针。
💡 学习建议(符合大厂要求)
-
优先掌握智能指针 :
cppauto ptr = std::make_unique<int>(42); // 自动管理内存 -
拒绝裸指针:除非在C++核心库或特定系统级代码中
-
边界检查 :所有数组/指针操作必须包含
< size检查 -
语义明确 :用
const和constexpr提升代码可读性 -
工具链必备 :
valgrind(内存检测)AddressSanitizer(运行时检测越界)Clang-Tidy(静态代码分析)
通过以上实践,您将掌握工业级C++内存管理能力,这正是阿里、腾讯、字节等大厂C++岗位的核心考察点。
最后忠告 :指针是C++的双刃剑------用得好是利器,用得差是灾难。在大厂,安全、可维护、可测试的代码永远比"炫技"更重要。
运行结果:
