24

golang的值类型,指针类型和引用类型&值传递&指针传递

 3 years ago
source link: https://studygolang.com/articles/29675
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一,变量类型

变量分为值类型,指针类型和引用类型。

以如下变量定义和赋值语句为例:

package main

import(
    "fmt"
)

func main(){

    var a int = 1
    p := &a
    fmt.Printf("%v\n", a)
    fmt.Printf("%p\n", &a)
    fmt.Printf("%p\n", p)
    fmt.Printf("%p\n", &p)
}

output:

1
0xc000092000
0xc000092000
0xc00008c010
mYBRr2V.png!web

值类型变量和引用类型变量

  • 值类型变量a,值为1,存储变量a的内存地址为0xc000092000
  • 指针类型 &a,某个变量的内存地址,即a的地址0xc000092000。
  • 引用类型p,某个变量的地址的别名。
    • 值为某个变量的内存地址(p的值为变量a的内存地址0xc000092000)
    • 其本身作为一个变量也有自己的存储内存地址0xc00008c010。

在golang中故意淡化了指针的概念,我们只需要关注值类型和引用类型就可以 。你在官方介绍中也很少看到指针类型这一概念。

二,golang中的值类型变量和引用类型变量

  • 值类型变量:除开slice,map,channel类型之外的变量都是值类型
  • 引用类型变量:slice,map,channel这三种

三,值类型和引用类型的区别

我们以值类型struct和引用类型map的区别在哪里。

首先定义两种类型的结构及变量:

type Student1 struct{
    Age int32
    Name string
}
type Student2 map[string]int

func main(){
    var s1 Student1
    var s2 Student2
}

1,零值不同

  • 指针类型的变量,零值都是nil。
  • 值类型的变量,零值是其所在类型的零值。
    • int32类型的零值是0
    • string类型的零值是""
    • bool类型的零值是false
    • 符合结构struct类型的零值是其每个成员的零值的组合
fmt.Println(s1) //{0 }
fmt.Println(s2) //map[] 
fmt.Println(s1 == nil) //panic,提示cannot convert nil to type Student1
fmt.Println(s2 == nil) //true

发现map的零值是nil,struct的零值是Student1{0, ""},不是nil,而且struct类型和nil是两种不同的类型,不能比较。

2,变量申明后是否需要初始化才能使用

  • 指针类型的变量,需要初始化才能使用。(slice是一个特例,slice的零值是nil,但是可以直接append)
  • 值类型的变量,不用初始化,可以直接使用
s1.Name = "minping"
s1.Age = 30
fmt.Println(s1) //{30 minping}

s2["minping"] = 30 // panic: assignment to entry in nil map
fmt.Println(s2)

发现struct声明后可以直接使用直接赋值,但是map就不行,赋值的时候提示我们nil map不能赋值,需要先初始化。

3,初始化方法不同

  • 值类型的变量,其实不需要初始化就可以使用。如果有良好的代码习惯,使用前进行初始化也是非常提倡的。
    • 基本类型的初始化非常简单:
      • var i int; i = 1;
      • var b bool; b = true
      • var s string; s = ""
    • 符合类型struct的初始化有两种:
      • s1 = Student1{}
      • s1 = new(Student1)
s1 := Student1{} //{0 }

s1 := new(Student1) //&{0 }
  • 引用类型的变量,初始化方式也不一样:
    • slice类型,用make,new,{}都可以
    • map类型,用make,new,{}都可以
    • channel类型,只能用make活着new初始化
//map可以用{},make,new三种方式初始化
s2 := Student2{} //map[]
s2 := make(Student2) //map[]
s2 := new(Student2) //↦[]

//slice可以用{},make,new,但是make的时候需要带len参数
type S3 []string
s3 := S3{} //[]
s3 := new(S3) //&[]
s3 := make(S3, 10) //[         ]

//channel只能用make或者new
type Student4 chan string
s4 := new(S4) //0xc000096000
s4 := make(S4) //0xc000082008
s4 := S4{} //编译器报错:invalid type for composite literal: Student4

四,make和new的区别

从上面初始化时用make和new的结果就可以看出来:

  • make返回的是对象。
    • 对值类型对象的更改,不会影响原始对象的值
    • 对引用类型对象的更改,会影响原始对象的值
  • new返回的是对象的指针,对指针所在对象的更改,会影响指针指向的原始对象的值。

五,golang没有引用传递,都是值传递

但是拷贝得到的引用类型变量的值,和被传入调用函数的原始引用类型变量的值,是一样的,即指向的是同一个变量的地址
7 type Student struct{
  8
  9     Age int
 10 }
 11
 12 func main(){
 13
 14     s := Student{30}
 15     modify1(s)
 16     fmt.Println(s) //{30}
 17
 18     modify2(&s)
 19     fmt.Println(s) //{32}
 20 }
 21
 22 func modify1(s Student){
 23     s.Age = 31
 24 }
 25
 26 func modify2(s *Student){
 27     s.Age = 32
 28 }

六,参考

go-pointers-vs-references

欢迎关注我们的微信公众号,每天学习Go知识

FveQFjN.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK