指针类型#
任何程序数据载入内存后,在内存都有内存的地址这就是指针。而为了保存一个数据在内存中的地址,就需要指针变量。一个指针的值是一个变量的地址。一个指针对应变量在内存中的存储位置。
例如:
x := 1 // 声明int类型变量x
p := &x // &x用于获取变量x的内存地址,返回一个指向x的指针p
fmt.Println(*p) // *p用户获取指针p指向变量的值
*p = 2 // 可以重新给*p指针赋值
fmt.Println(x) // "2"输出:
1
2 用 var x int 声明语句声明一个变量,&x 表达式(取x变量的内存地址)将产生一个指向该整数变量的指针,指针对应的数据类型是 *int,名字为 p ,可以理解为:“p指针指向变量x”或者“p指针保存了x变量的内存地址”。同时 *p 表达式对应p指针指向的变量的值。一般 *p 表达式读取指针指向的变量的值,这里为int类型的值,同时因为 *p 对应一个变量,所以该表达式也可以出现在赋值语句的左边,表示更新指针所指向的变量的值。
注意:因为指针包含了一个变量的地址,因此如果将指针作为参数调用函数,那将可以在函数中通过该指针来更新变量的值
&x:获取变量x的内存地址,返回一个指向x的指针*p:获取指针p指向变量的值
看一下 b := &a 的图示:

总结: 取地址操作符 & 和取值操作符 * 是一对互补操作符,& 取出地址,* 根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址
&操作,可以获得这个变量的指针变量 - 指针变量的值是指针地址
- 对指针变量进行取值
*操作,可以获得指针变量指向的原变量的值
指针传值
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
}new和make#
先来看一个例子:
func main() {
var a *int
*a = 100
fmt.Println(*a)
var b map[string]int
b["wangpengliang"] = 100
fmt.Println(b)
}执行上面的代码会引发 panic,为什么呢? 在Go语言中对于引用类型的变量,在使用的时候不仅要声明,还要为它分配内存空间,否则值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好内存空间。Go语言中 new 和 make 是内建的两个函数,主要用来分配内存。
这里 panic原因在于:
var a *int 只是声明了一个指针变量 a 但是没有初始化,指针作为引用类型需要初始化后才会拥有内存空间,才可以赋值。比如:
var a *int
a = new(int)var b map[string]int 只是声明变量 b 是一个 map 类型变量而未初始化。需要初始化后才可以赋值,比如:
var b map[string]int
b = make(map[string]int, 10)new#
new 是一个内置的函数,函数签名如下:
func new(Type) *Type其中,
Type表示类型,new函数只接受一个参数,这个参数是一个类型*Type表示类型指针,new函数返回一个指向该类型内存地址的指针
使用 new 函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。比如:
func main() {
a := new(int)
b := new(bool)
fmt.Printf("%T\n", a) // *int
fmt.Printf("%T\n", b) // *bool
fmt.Println(*a) // 0
fmt.Println(*b) // false
} make#
make 也是用于内存分配的,与 new 区别在于它只用于 slice、map以及 channel 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。
因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
函数签名如下:
func make(t Type, size ...IntegerType) Typenew与make的区别#
- 二者都是用来做内存分配的。
make只用于slice、map、channel的初始化,返回这三个引用类型本身new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针