本质区别

  • 值类型:
    内存中变量存储的是具体的值,比如: var x int x存放的是具体的int值,但是变量在内存中的地址可以通过 &num来获取
  • 引用类型:
    变量直接存放的就是一个地址值, 这个地址值指向的空间存的才是值。比如 var prt *int
    • 差异性
      相同的程序在不同的机器上执行后可能也会有不同的内存地址
    • 连续内存
      同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。(下面这句话不懂)

值类型、引用类型都包括什么?

  • 值类型(6):
    int、float、 bool、string、数组、 结构体 struct
  • 引用类型(5):
    指针、切片slice、集合map、通道chan、interface

值类型和引用类型的使用特点

值类型 引用类型
存储地址 值类型直接存放值,内存通常在栈中分配 引用类型变量存储的地址(也就是通过指针访问类型里面的数据),通常真正的值在堆上分配。当没有变量号用这个地址的时候,该值会被gc回收。
拷贝 值拷贝,克隆体和本体那就是两个东西了 引用拷贝,克隆体和本体是一个东西
比如:切片和底层数组
做组成元素 (个人感受) 值类型可以当作数组/切片/集合等引用类型 的元素 但是引用类型不能成为引用类型的元素,尤其是,我们总是把结构当成和切片这样的存在(虽然确实挺像)
a与b 值类型 引用类型
a=b a与b不相关 a与b共享一个底层数据
copy(a,b) copy函数只支持切片 a与b不相关

实例分析

数组与切片

定义了一个数组a,它是值类型,复制给b是copy,当b发生变化后a并不会发生任何变化,程序的执行结果如下所示:

  1. func main{
  2. a:=[5]int{1,2,3,4,5}
  3. b:= a
  4. b[2]= 8
  5. fmt.Println(a, b) //结果为:[1,2,3,4,5] [1,2,8,4,5]
  1. func main0 {
  2. a :=[int{1,2,3,4,5}
  3. b:= a
  4. b[2]= 8
  5. fmt.Println(a, b) //结果为[1,2,8,4,5] [1,2,8,4,5]
  6. }

结构体和集合map

  1. package main
  2. type Counter struct {
  3. count int
  4. }
  5. func add2(s map[string]Counter) {
  6. counter := s["count"]
  7. counter.count++
  8. }
  9. func add3(s map[string]*Counter) {
  10. counter := s["count'"]
  11. counter.count++
  12. }
  13. func main() {
  14. m2 := map[string]Counter{" count":Counter{count: 20}}
  15. add2(m2)
  16. //temp := m2["count"]
  17. //temp.count+ +
  18. println(m2["count"].count)
  19. m3 := map[string]*Counter{"count":&Counter{20}}
  20. add3(m3)
  21. println(m3["count"].count)
  22. }

:::info 理想结果为:

:::

20

21

:::info 解释

:::

  • m2,m3是结构体和map的结合。m3是结构体型指针和map的结合.
  • m2不会改变,主要是因为add2中使用:=重新声明了counter,由于是对象是值类型,所以也就类似于第一个实例那样, 只M2不会改变,主要是因为add2中使用:=重新声明了计数器,由于是对象是值类型,所以也就类似于第一个实例那样,只是单纯的进行了值拷贝。
  • m3是的add3中虽然看起来一样。 但实际上counter是一个指针, 所以对counter进行操作会导致原来的数值也发生改变。M3是的加法3中虽然看起来一样。但实际上计数器是一个指针,所以对计数器进行操作会导致原来的数值也发生改变。