指针类型#

任何程序数据载入内存后,在内存都有内存的地址这就是指针。而为了保存一个数据在内存中的地址,就需要指针变量。一个指针的值是一个变量的地址。一个指针对应变量在内存中的存储位置。

例如:

	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语言中 newmake 是内建的两个函数,主要用来分配内存。

这里 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 区别在于它只用于 slicemap以及 channel 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。

因为这三种类型就是引用类型,所以就没有必要返回他们的指针了

函数签名如下:

func make(t Type, size ...IntegerType) Type

new与make的区别#

  1. 二者都是用来做内存分配的。
  2. make 只用于slicemapchannel 的初始化,返回这三个引用类型本身
  3. new 用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针