golang指针的简单理解

文章目录

  • 在学习指针之前
  • 指针类型

在学习指针之前

  • go里面所有的基础数据结构都是值类型的(变量存储的是对应类型的值,这种类型叫值类型),即在赋值变量的时候,会直接拷贝一份数据,两个变量的值是一样的,但是地址是不同的,如数组,基础数据了类型都是值类型的,好处就是隔离,生命周期相对好管理,但是开销会大,在传参,赋值的时候都要进行内存拷贝
  • 而指针类型,也叫做引用类型等等,他的内存里是存的一个地址,通过调用指针别名来访问这个地址指向的内存的数据。指针类型在传递的时候是不需要拷贝数据的,只拷贝指针地址即可,开销相对小,但同时也不好做到隔离和回收

指针类型

  • 每个变量都有内存地址,通过操作变量来操作对应大小的内存,但是不是每个值都有地址,使用指针可以间接读取或者更新变量的值
  • 刚才说的每个变量都有一个地址,聚合类型变量的成员(结构体成员或者数组中的元素)也都是变量,也都有地址
  • &为取地址符
  • 指针类型的变量存储的是一个地址,所以又叫指针类型,或者引用类型,操作指针类型就是操作对应地址的内存
    golang指针的简单理解_第1张图片golang指针的简单理解_第2张图片
  • 定义,如果初始化时候不赋值,指针类型默认是个空地址,可以用if p==nil来判断
var 变量名 *T
// 这里变量名就是指针变量 的变量名,这里T是可以任意类型的,也就是说指针类型可以是int 的指针类型,也可以是string的指针类型

b := 255 
var a *int = &b
fmt.Printf("%T\n", a)
fmt.Printf("%p\n", a)
fmt.Printf("%p,%p\n", &a,a) // 这里注意指针类型的变量也是有地址的
  • *a 是操作指针的值,即*a 其实就是把a 指针变量指向的内存地址里面存的东西拿出来就是*a,*a其实可以理解成为一个引用的基础数据结构的变量,而*a也可以理解为是一个指针别名
var a int = 100
var b *int = &a 
fmt.Printf("b指向的地址存储的值为:%d\n", *b)
// 这里输出的结果是100,可以简单理解一下
*b = 10000
fmt.Printf("a的值为:%d\n", a)
// 这里输出的结果是10000,可以简单理解一下
*b++
fmt.Printf("b指向的地址存储的值为:%d\n", *b)
// 这里输出的结果是10001,可以简单理解一下
  • 每次使用变量的地址或者复制一个指针,我们就创建了新的别名或者方式来标记同一变量。例如*b 是a的别名。指针别名允许我们不用变量的名字来访问变量,这一点事非常有用的,但是他是双刃剑: 为了 找到所有访问变量的语句,需要知道所有的别名。但是不仅指针会产生别名,当复制其他的一用类型(slice,ma盘,通道,甚至包含这里引用类型的结构体,数组和接口)的值的时候,都会产生别名。
  • 不要传递指向数组的指针给函数,而是使用切片
package main

import (  
        "fmt"
)

func modify(arr *[3]int) {  
        (*arr)[0] = 90
        //也可以简写为下面这样
        arr[0] = 90
}

func main() {  
        a := [3]int{89, 90, 91}
        modify(&a)
        fmt.Println(a)
}

// 但是用指针的方式不推荐,可以用切片直接传递给函数进行修改
package main

import (  
        "fmt"
)

func modify(sls []int) {  
        sls[0] = 90
}

func main() {  
        a := [3]int{89, 90, 91}
        modify(a[:])
        fmt.Println(a)
}
// 这里可能难理解为什么(*arr)[0] 可以简写成arr[0],看下面的例子
package main

import "fmt"

func main() {
        aaa := [3]int{1,2,3}
        var bbb *[3]int = &aaa
        ccc := aaa[:]
        fmt.Printf("%v\n%p\n%v\n%p\n%v\n", aaa, bbb, *bbb, ccc, ccc)
        fmt.Printf("%d\n%d\n%d\n", bbb[0], (*bbb)[0], ccc[0])
}
// 输出结果
[1 2 3]
0xc420094000
[1 2 3]
0xc420094000
[1 2 3]
1
1
1
// arr[0]这里我们 想的是那个值类型的变量,而这里arr *[3]int 的是指针类型的变量,所以这是两种变量了,而我们的slice 类型就是一种指针类型,所以 (*arr)[0]可以参照slice来理解。
// 我们只刚才概括了一点,*arr其实是指针别名,(*arr)[0] 就像是slice类型的ccc[0]是一样的,因为函数的形参里面arr是一个指针类型,所以*arr是他的指针别名,arr[0]中的arr同时也是他的指针别名。就像我们测试ccc 和 bbb的一样,他们两个因为都是指针类型,所以这两个名字都是指针别名。
// 所以我们用arr[0]这个指针别名就像用slice 的ccc[0]这个指针别名一样,(*arr)[0]这里不过就是我们定义的另一个指针别名

你可能感兴趣的:(golang)