1 指针
1.1 Go语言中指针介绍
指针是程序载入到内存中的所对应的地址,为了保存数据在内存中对应的地址,就有了指针变量。指针在内存中的示意图如下:
Go语言中的指针不能进行偏移和运算,是安全指针。在Golang中只有&
(取址)和*
(取值)两个操作。
1.2 指针和地址的区别
地址:是内存地址(用字节来描述的内存地址)
指针:指针存储执行类型数据在内存空间中对应的地址。比如说string类型的指针,其只能存储string变量类型的指针
1.3 指针和指针类型
如同上面所说,指针是地址,但是地址不是指针,因为指针有其对应的类型,如*int
、float64
、*string
等。
取变量指针的语法如下:
ptr := &v // v的类型为T
- v:代表被取地址的变量,类型为T
- ptr:用于接收地址的变量,ptr的类型就是
*T
,称其为T的指针类型
代码演示:
func main() {
var a int
fmt.Println(a) //0
b := &a // 取变量a的内存地址
fmt.Printf("b=%v
", b) //b=0xc00000a0c0
fmt.Printf("type b:%T
", b) //type b:*int
c := "tom"
// b = &c //go语言是静态类型,变量只能存储一种类型的地址
// golang中的地址只能读不能操作
fmt.Printf("&c=%v
", &c) //&c=0xc0000321f0
d := 100
b = &d
fmt.Println(b) //0xc00000a0d8
// *取地址对应的值
fmt.Println(*b) //100
// 指针可以做逻辑判断
fmt.Println(b == &d) //true
}
对于上述代码的说明:
b := &a
在内存中的示意图:
- 语法糖:因为go语言中的指针不支持地址的修改,所以编译器会默认修改指针对应的值。也就是说,如果有一个指针类型的变量
ptr
,对其进行一些运算(加法运算),在使用的过程中*ptr + 2
等价于ptr + 2
。
1.4 指针的使用场景
指针通常会在函数之间的传参时使用。函数在传参时是通过值传参的,比如参数是数组,在传参的过程中形参会对实参的值拷贝一份存储在形参变量中,如果这个数组很大时,那么这个过程就很浪费内存,这时就可以使用指针进行传参,不论数组有多大,传参过程中所耗费的内存空间都是固定的。
指针传值示例:
func modify1(x int) {
x = 100
}
func modify2(x *int) {
*x = 100
}
func main() {
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}
2 new和make
在Go语言中对于值类型的声明不需要分配内存空间,因为它们在声明的时候已经默认分配好了内存空间;而对于引用类型的以下变量,不仅要声明它,还需对其进行内存空间的分配,比如:
var a *int
的意思是a是一个int
类型的指针,但是在内存中没有其存放的地方,应当声明其存放的地址,以及该地址对应的值(初始化)。
Golang中的new
和make
都是内建函数,主要用来分配内存。
2.1 new
new
函数主要给一些值类型(int系列,数组)的指针分配内存空间,其函数签名如下:
func new(Type) *Type
其中,
Type
表示类型,new
函数只接受一个参数,这个参数是一个类型*Type
表示类型指针,new
函数返回一个指向该类型内存地址的指针
new
函数不太常用,使用new
函数得到的是一个值类型的指针,并且该指针对应的值为该类型的零值。如下示例:
func main() {
a := new(int)
b := new(bool)
fmt.Printf("%T
", a) // *int
fmt.Printf("%T
", b) // *bool
fmt.Println(*a) // 0
fmt.Println(*b) // false
}
在声明了一个指针变量后,需要对其进行初始化。按照如下方式使用new
函数对a进行初始化以及赋值操作:
func main() {
var a *int
a = new(int)
*a = 10
fmt.Println(*a)
}
2.2 make
make
函数主要给一些引用类型(slice,map,channel)进行初始化,该函数返回的类型就是引用类型本身,而不是指针类型。因为其本身就是引用类型,所以就没有必要返回指针。
make
的函数签名如下:
func make(t Type, size ...IntegerType) Type
make
函数使用示例:
func main() {
var b map[string]int //声明了一个map类型的变量b
b = make(map[string]int, 10) //开辟存储长度为10的map类型
b["age"] = 20
fmt.Println(b)
}
2.3 new和make的区别
- 二者都是用来做内存的分配
make
用于对引用类型的初始化,返回的是引用类型的本身new
用于值类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针