指针传递传递的是内存地址的副本还是指针的副本

在 Go 语言中,指针传递传递的是指针的副本,而这个指针本身是一个内存地址的值。所以更精确地说,传递的是内存地址的副本

让我详细解释一下:

  1. 指针是什么?

    • 在 Go 中,指针是一个变量,它存储的是另一个变量的内存地址。比如 var p *intp 是一个指针,指向某个整数的内存地址。
    • 内存地址本身是一个值(通常是 64 位或 32 位整数,取决于系统架构)。
  2. 指针传递的过程

    • 当你将一个指针传递给函数时,比如 func foo(p *int),Go 会将这个指针的值(即内存地址)复制一份,传递给函数。
    • 函数接收到的 p 是原始指针的副本,但这个副本仍然指向相同的内存地址。
    • 因为是指向同一地址,函数可以通过解引用(*p)修改原始值。
  3. 为什么说是“内存地址的副本”?

    • 传递的不是原始指针变量本身,而是它的值(内存地址)的副本。
    • 这意味着函数内部如果修改了指针变量本身(比如让 p 指向另一个地址),不会影响调用处的原始指针。但如果修改的是指针指向的内容(*p),则会影响原始值。

示例代码

package main

import "fmt"

func modifyPointer(p *int) {
    fmt.Printf("函数内指针地址: %p\n", p) // p 是指针的副本
    *p = 100                        // 修改指针指向的内容
}

func reassignPointer(p *int) {
    x := 200
    p = &x // 修改 p 本身,让它指向新地址
}

func main() {
    a := 10
    fmt.Printf("原始指针地址: %p\n", &a)
    modifyPointer(&a)
    fmt.Println("修改后 a 的值:", a) // 输出 100

    reassignPointer(&a)
    fmt.Println("尝试重新赋值后 a 的值:", a) // 输出 100,未改变
}

输出解释

  • modifyPointer(&a):函数内的 p&a 的副本,指向同一个地址,修改 *p 会改变 a 的值。
  • reassignPointer(&a):函数内的 p 是副本,重新赋值 p = &x 只影响副本,不影响原始的 &a,所以 a 不变。

结论

  • 指针传递传递的是指针的副本,这个副本是一个内存地址的值。
  • 函数内部可以通过这个地址修改原始数据,但如果修改指针本身(让它指向新地址),不会影响外部的原始指针。

我用一个通俗的比喻来解释 Go 中的值传递指针传递,让你更容易理解。

比喻:寄快递

想象你有一本书,想要让朋友帮你修改这本书的内容。

  1. 值传递(Pass by Value)

    • 你把整本书复印了一份,寄给了朋友(快递的是书的副本)。
    • 朋友收到这本复印的书,在上面随便改,比如涂鸦、撕页,但你的原书一点没变,因为他改的是复印件。
    • 特点:朋友拿到的是独立副本,改动不影响你的原书。复印整本书可能费时费力(如果书很厚,复制开销大)。

    对应代码

    func modifyBook(book string) {
        book = "改了内容"
    }
    book := "原书"
    modifyBook(book)
    fmt.Println(book) // 还是 "原书"
    
  2. 指针传递(Pass by Pointer)

    • 你不寄整本书,而是写了一张纸条,上面写着“我的书在家里,地址是 XX 街 YY 号”,然后把这张纸条复印了一份寄给朋友。
    • 朋友收到纸条副本,知道地址没变,顺着地址找到你的原书,直接在原书上改内容(比如加了几页笔记)。
    • 你回家一看,书真的被改了,因为他操作的是原书。
    • 特点:寄纸条很轻松(复制地址开销小),但朋友能直接改你的原书。纸条是副本,但指向的还是同一个地址。

    对应代码

    func modifyBookByPointer(book *string) {
        *book = "改了内容"
    }
    book := "原书"
    modifyBookByPointer(&book)
    fmt.Println(book) // 输出 "改了内容"
    
  3. 再加一个情况:朋友改了纸条上的地址

    • 如果朋友收到纸条副本后,没去改你的书,而是把纸条上的地址改成他自己的书(比如“ZZ 街 WW 号”),然后在自己的书上改内容。
    • 这时你的原书还是没变,因为他改的是他自己的书,纸条副本的指向变了,但不影响你手里的原纸条和原书。

    对应代码

    func reassignPointer(book *string) {
        newBook := "新书"
        book = &newBook // 改了指针副本的指向
    }
    book := "原书"
    reassignPointer(&book)
    fmt.Println(book) // 还是 "原书"
    

通俗总结

  • 值传递:寄的是书的复印件,朋友怎么改都不影响你的原书。
  • 指针传递:寄的是地址的复印件,朋友按地址找到原书,能直接改你的书。但如果朋友把地址改成别的,改的是他手里的纸条副本,不影响你的原书。
posted @ 2025-03-04 14:46  仁义礼智信的  阅读(25)  评论(0)    收藏  举报