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++函数参数"值传递"特性的直接结果

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

相关推荐
神仙别闹12 小时前
基于QT(C++)实现学本科教务系统(URP系统)
数据库·c++·qt
deng-c-f12 小时前
Linux C/C++ 学习日记(49):线程池
c++·学习·线程池
ulias21213 小时前
C++ 的容器适配器——从stack/queue看
开发语言·c++
daidaidaiyu13 小时前
FFmpeg 关键的结构体
c++·ffmpeg
欧特克_Glodon13 小时前
C++医学图像处理经典ITK库用法详解<一>:图像输入输出模块功能
c++·图像处理·itk
一个不知名程序员www14 小时前
算法学习入门---priority_queue(C++)
c++·算法
Pafey15 小时前
C++的左值引用、右值引用以及转发和完美转发
c++
CoderCodingNo15 小时前
【GESP】C++三级真题 luogu-B4414 [GESP202509 三级] 日历制作
开发语言·c++·算法
晨曦夜月16 小时前
笔试强训day7
开发语言·c++·算法
木心爱编程16 小时前
【Qt 5.14.2 新手实战】QTC++入门筑基——按钮与标签联动:QPushButton + QLabel 实现图片切换器
java·c++·qt