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

指针参数传递的深层原理

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

1. 指针的内存表示

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

复制代码
假设有:
int a = 10;
int* p = &a;

内存布局可能如下:

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

2. 函数参数传递机制

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

cpp 复制代码
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汇编示例:

assembly 复制代码
; 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:二级指针

cpp 复制代码
void alloc(int** pp) {
    *pp = new int(10);  // 解引用二级指针修改原始指针
}

int main() {
    int* p;
    alloc(&p);  // 传递指针的地址
}

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

cpp 复制代码
void alloc(int*& pRef) {
    pRef = new int(10);  // 直接修改引用绑定的指针
}

int main() {
    int* p;
    alloc(p);
}

8. 总结要点

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

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

相关推荐
李匠202415 分钟前
C++负载均衡远程调用学习之 Dns-Route关系构建
运维·c++·学习·负载均衡
jiunian_cn1 小时前
【c++】模板详解
开发语言·c++·visual studio
双叶8361 小时前
(51单片机)LCD显示红外遥控相关数据(Delay延时函数)(LCD1602教程)(Int0和Timer0外部中断教程)(IR红外遥控模块教程)
c++·单片机·嵌入式硬件·51单片机
jerry6091 小时前
算法四 习题 1.3
数据结构·c++·算法
工藤新一¹2 小时前
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 19)
开发语言·c++·游戏引擎·游戏开发·sdl
小冯的编程学习之路2 小时前
【C++】:C++17新特性
c语言·开发语言·c++·算法
wuqingshun3141592 小时前
蓝桥杯 10. 凯撒加密
c++·算法·职场和发展·蓝桥杯·深度优先
汉克老师2 小时前
GESP2024年6月认证C++八级( 第三部分编程题(2)空间跳跃)
c++·算法·图论·gesp八级·gesp8级
Tanecious.3 小时前
C++--入门基础
java·开发语言·c++