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

c++ 指针参数传递的深层原理

指针参数传递的深层原理

理解为什么可以修改指针指向的内容但不能直接修改指针本身,需要深入理解指针在内存中的表示方式和函数参数传递机制。

1. 指针的内存表示

指针本质上是一个变量,它存储的是另一个变量的内存地址。在内存中:

假设有:
int a = 10;
int* p = &a;

内存布局可能如下:

地址   | 变量名 | 值
0x1000 | a      | 10    (int)
0x2000 | p      | 0x1000 (指向a的指针)

2. 函数参数传递机制

当传递指针给函数时,发生的是值传递,即传递指针值的拷贝:

void func(int* ptr) {// ptr是p的副本,指向相同地址*ptr = 20;  // 修改指向的内容ptr = nullptr; // 只修改副本
}int main() {int a = 10;int* p = &a;func(p);// p仍然指向a,a的值变为20
}

内存中的变化过程:

  1. 调用前:

    [main栈帧]
    0x3000: p = 0x1000[全局内存]
    0x1000: a = 10
    
  2. 调用func§时:

    • 创建ptr副本(值=0x1000)
    [func栈帧]
    0x4000: ptr = 0x1000 (p的拷贝)
    
  3. 执行*ptr = 20:

    • 通过ptr找到0x1000位置
    • 修改该位置的值
    [全局内存]
    0x1000: a = 20
    
  4. 执行ptr = nullptr:

    • 只修改func栈帧中的副本
    [func栈帧]
    0x4000: ptr = nullptr
    
    • main中的p不受影响

3. 为什么可以修改指向的内容

因为指针副本和原指针存储的是相同的地址值,所以通过这个地址可以访问和修改同一块内存:

原始指针 p: 0x1000 → [a的内存]
副本指针 ptr: 0x1000 → [同一块a的内存]

解引用操作 (*ptr) 是通过地址访问内存,不受指针副本的影响。

4. 为什么不能修改指针本身

因为函数内部操作的是指针的副本,对副本的赋值不会影响原始指针:

原始指针 p: 0x1000
副本指针 ptr: 0x1000 → 修改为nullptr

修改ptr只是改变了函数栈帧中的副本,main中的p保持不变。

5. 类比说明

可以把指针想象成一个便签纸:

  • 原始指针:你手中写着地址的便签
  • 指针参数:复印了这个便签给朋友
    • 朋友可以用便签上的地址找到你家并重新装修(修改内容)
    • 朋友如果扔掉或修改他的复印件(修改指针本身),不会影响你手中的原件

6. 底层汇编视角

x86-64汇编示例:

; C代码: *ptr = 20;
mov rax, QWORD PTR [rbp-8]  ; 从栈加载ptr的值(地址)到rax
mov DWORD PTR [rax], 20     ; 向该地址写入20; C代码: ptr = nullptr;
mov QWORD PTR [rbp-8], 0    ; 只修改栈上的副本

可以看到修改内容是通过指针值间接寻址,而修改指针本身只是修改局部变量。

7. 正确修改指针的方法

如果需要修改指针本身(而不仅是指向的内容),必须传递指针的地址或引用:

方法1:二级指针

void alloc(int** pp) {*pp = new int(10);  // 解引用二级指针修改原始指针
}int main() {int* p;alloc(&p);  // 传递指针的地址
}

方法2:指针引用(C++推荐)

void alloc(int*& pRef) {pRef = new int(10);  // 直接修改引用绑定的指针
}int main() {int* p;alloc(p);
}

8. 总结要点

  1. 指针参数传递的是指针值的拷贝(地址值的副本)
  2. 通过副本指针可以修改原始数据(因为地址相同)
  3. 修改副本指针本身(改变它存储的地址)不影响原始指针
  4. 要修改指针本身需要传递指针的指针或指针的引用
  5. 这是C/C++函数参数"值传递"特性的直接结果

理解这一机制对于正确使用指针和避免内存错误至关重要。

http://www.xdnf.cn/news/3745.html

相关文章:

  • [Vue]props解耦传参
  • 我写了一个分析 Linux 平台打开文件描述符跨进程传递的工具
  • 动态规划之多状态问题1
  • AIStarter开发者手记:一键部署本地大模型,跨平台整合包技术解析
  • 63常用控件_QSlider的使用
  • STL之list容器
  • 计算机基础:二进制基础17,八进制减法
  • 大模型中常见的精度类型及区别​
  • 论微服务架构及其应用
  • 传奇各职业/战士/法师/道士/勋章爆率及出处产出地
  • 54、【OS】【Nuttx】编码规范解读(二)
  • 130. 被围绕的区域
  • (1)大模型的提示词工程实践技巧---LLM输出配置详解
  • 数字孪生赋能智慧城市:从概念到落地的深度实践
  • 【文献阅读】中国湿地随着保护和修复的反弹
  • DeepSeek眼中的文明印记:金刚经
  • 004 树与二叉树:从原理到实战
  • Baklib赋能企业知识管理数字化转型
  • MCP 协议知识分享指南
  • VS调试技巧
  • 网站即时备份,网站即时备份的方法有哪些
  • 简介QML中的Canvas
  • 机器学习入门-线性回归模型/损失函数/梯度下降
  • 【WZOI】【题解】【质数密度】质数密度题解报告
  • 旋转矩阵公式理解
  • 【云备份】服务端数据管理模块设计与实现
  • 嵌入式 GCC 编译工具链:32 位与 64 位助力高效开发
  • [UVM]UVM中reg_map的作用及多个rem_map的使用案例
  • 【C++篇】类和对象(上)
  • 饱和蒸汽再生数据采集挥发性有机物(VOCs)吸附脱附实验装置