V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
beego
waibunleung
V2EX  ›  Go 编程语言

关于 go 数组指针的疑问

  •  1
     
  •   waibunleung · 2018-07-31 13:04:35 +08:00 · 2061 次点击
    这是一个创建于 1217 天前的主题,其中的信息可能已经有所发展或是发生改变。

    面在学 go 指针的时候,认为 指针变量存放的是 内存地址值 , *操作符就是能拿到该内存储存的值

    然后将指针结合到数组的时候(数组指针),发现直接用指针就能操作数组了,而不需要用*

    普通变量指针操作

    package main
    
    import "fmt"
    
    func main(){
    	a := 10
    	p := &a
    	*p = 20
    	fmt.Print(a)  //20
    }
    

    上面的一般都没什么问题,然后是数组指针

    package main
    
    impotr "fmt"
    
    func main(){
    	pArr := [3]int{12,2,9}
    	pArr[0] = 16
    	pAdd := &pArr
    	(*pAdd)[0] = 444
    	
    	fmt.Printf("pArr[0] %v\n",pArr[0])
    	fmt.Printf("pAdd[0] %v\n",pAdd[0])
    	fmt.Printf("pAdd %v\n",pAdd)
    	fmt.Printf("pArr  %v\n",pArr)
    	fmt.Printf("pAdd 指向的东西 %v\n",*pAdd)
    	fmt.Printf("pArr[0]的地址  %v\n",&pArr[0])
    	fmt.Printf("pAdd[0]的地址  %v\n",&pAdd[0])
    	fmt.Printf("(*pAdd)[0]的地址  %v\n",&(*pAdd)[0])
    	
    	/*output
            pArr[0] 444
            pAdd[0] 444
            pAdd &[444 2 9]
            pArr  [444 2 9]
            pAdd 指向的东西 [444 2 9]
            pArr[0]的地址  0xc04200c2e0
            pAdd[0]的地址  0xc04200c2e0
            (*pAdd)[0]的地址  0xc04200c2e0
    	*/
    }
    

    注意到上面,

    直接操作数组 pArr[0] = 16 我可以理解 通过指针找到数组进而操作数组 (*pAdd)[0] = 16 我也可以理解

    但是~!

    直接通过指针就能操作到原来的数组是什么操作? pAdd[0] = 16 这我就不是很理解了... 这样子写 字面意思上 操作的是指针,但是原来的数组也跟着改变了,输出后发现大家指向的都是同一个地址值

    pArr[0]的地址 0xc04200c2e0
    pAdd[0]的地址 0xc04200c2e0
    (*pAdd)[0]的地址 0xc04200c2e0

    ps:题主没有 c 这种指针型语言的基础,望理解

    最后想问,用指针直接操作数组这样的情况要怎么去理解呢?

    14 条回复    2018-07-31 22:51:10 +08:00
    KeepPro
        1
    KeepPro   2018-07-31 13:33:30 +08:00
    最后想问,用指针直接操作数组这样的情况要怎么去理解呢?
    其实数组指针指向的是数组的第一个元素的地址。所以你输出的都是同一个地址
    lifespy
        2
    lifespy   2018-07-31 13:42:03 +08:00
    可能你看下数据结构就明白为什么了吧
    leemove
        3
    leemove   2018-07-31 14:03:59 +08:00
    `pAdd[0] = 16` 其实就是 `(*pAdd)[0] = 16`...go 语言不能直接操作指针的,只能操作指针指向区域吧.

    以上纯属不负责任猜测,有问题楼下大佬指正.
    hyyou2010
        4
    hyyou2010   2018-07-31 14:11:47 +08:00
    印象中(很多年不写了),在 C 语言里面,有两种对数组元素间接寻址的方法,但其实是一种:
    ArrayName[下标 or 偏移量]
    *pointerToArray[下标 or 偏移量]

    pAdd[0] = 16 --------- 你可能迷惑的是,这个相当于 " pointerToArray[下标 or 偏移量] ",怎么没有前面的*号了?

    按我的肤浅观察(我对 Go 的认识很肤浅),实际上是 Go 语言对此做了处理,这种处理,很可能基于这些认知:
    1,避免双重指针 " **pointerToPointer ",或更多重的指针
    2,指针变量的地址值是无意义的,所以你对指针变量取地址,结果得到的不是指针变量本身的地址,而是指针所指内存的地址,所以 pArr 和 pAdd 是一模一样的。这点可能让 C/C++同学很不爽

    通过这类简化和限制处理,大概能够避免常见的指针错误吧

    其实还是喜欢汇编 /C/C++,操纵的是真实的物理机器,完全掌控一切
    heimeil
        5
    heimeil   2018-07-31 14:13:47 +08:00
    go 里面为了方便程序员,有很多隐式操作,这里是隐式解引用了。
    lk0317
        6
    lk0317   2018-07-31 14:50:44 +08:00   ❤️ 2
    https://golang.org/ref/spec#Index_expressions

    For a of pointer to array type:

    a[x] is shorthand for (*a)[x]
    kkurs
        7
    kkurs   2018-07-31 15:23:05 +08:00
    @hyyou2010
    1. C 里面一样是 pointerToArray[index]的写法
    2. 你说的“对指针变量取地址,结果得到的不是指针变量本身的地址,而是指针所指内存的地址”根本就没这回事,上面的代码就没有打印 &pAdd,你可以试试看
    Leigg
        8
    Leigg   2018-07-31 15:27:16 +08:00
    5 楼真相了, 写几行代码你看一下,但是指针要理解的话,多看文档和实践,不是一下子你就能理解了,我感觉的话。
    ```
    package main

    import (
    f "fmt"

    )

    type stu struct {
    name string
    }

    var book = make(map[string]int64)

    //http 客户端编写
    func main(){
    //值类型示例: array 和 struct
    a := &[4]int64{1,2,44}
    student := new(stu)
    student.name = "狗朗" //这两行等效于 student = &stu{name: "狗朗"}

    //引用类型示例:slice 和 map
    s := &[]int64{4,5,6}
    book["瞎卡拉卡环游记"] = 666
    b := &book

    //值类型变量的显示解引用(操作指针)
    f.Println((*a)[0],(*student).name) // 输出:1 狗朗
    //值类型变量的隐式解引用(不操作指针)
    f.Println(a[0],student.name) // 输出:1 狗朗

    //引用类型变量的显示解引用(操作指针)
    f.Println((*s)[0], (*b)["瞎卡拉卡环游记"]) // 输出:4 666

    //引用类型变量的隐式解引用(操作指针)
    //f.Println(s[0],b["瞎卡拉卡环游记"]) --语法错误,因为引用类型->无隐式解引用特性
    }
    ```
    Leigg
        9
    Leigg   2018-07-31 15:27:59 +08:00
    http 那行自动忽略
    Leigg
        10
    Leigg   2018-07-31 15:31:55 +08:00
    倒数第三行括号内应是:不操作指针
    waibunleung
        11
    waibunleung   2018-07-31 16:15:59 +08:00
    @lk0317 兄弟你真是明灯啊,已感谢~
    hyyou2010
        12
    hyyou2010   2018-07-31 16:23:58 +08:00
    @kkurs 谢谢指正!

    仔细看了一下,对指针取地址以后确实是获取了指针的地址,而不是指针所指内容的地址,也即,跟 C 语言一致

    C 语言里面指针操作数组是 *(pointerToArray +下标) = newValue,不是方括号。多年不用,也是记忆不清
    d18
        13
    d18   2018-07-31 17:40:45 +08:00
    还是 c/c++好,写出来的代码是什么样子,执行的效果就是什么样子。
    现在的语言,有点过度封装了,天晓得一个简单的语句,背后隐藏了多少复杂的,不可告人的操作和深坑。
    merin96
        14
    merin96   2018-07-31 22:51:10 +08:00
    @d18 C 还行,C++就别这么说了八,坑最多的
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2342 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 15:29 · PVG 23:29 · LAX 07:29 · JFK 10:29
    ♥ Do have faith in what you're doing.