一、内存地址

在了解指针之前,我们需要先知道什么是内存地址。先看下面的代码,我们定义var i int = 10的变量

package main

import "fmt"

func main(){
	var i int = 10
	fmt.Println(i)
}

在我们定义变量的时候,他会在内存空间分配一块区域,来存放我们的值

但是这个内存空间 本身也是有一个地址的,那么我们怎么拿到这个内存地址呢 "&"

获取变量的内存地址

package main

import "fmt"

func main(){
	var i int = 10

	fmt.Println("i的内存地址=",&i) //通过在变量前面设置&
	                              //在这样就可以把变量i的内存地址取出来
}

返回

i的内存地址= 0xc00000a098

也就是说上面变量i 指定的内存地址 如下

 

二、指针

指针类型存放的是内存地址,通过指定内存空间来获取对应的值

想要定义一个指针的数据类型,只需要在基本数据类型前面加星号(*) 即可  如 *int

 案例

package main

import "fmt"

func main(){
	var i int = 10
    fmt.Println(&i)

	var ptr *int    //这里定义了一个int类型的指针
	ptr = &i        //指针只能存放内存地址,通过前面的&号将变量i的内存地址赋值给指针变量ptr

	fmt.Printf("Ptr=%v\n",ptr)  //直接输出ptr的话,我们只能得到指针变量里面存放的值,也就是变量i的内存地址
	fmt.Printf("Ptr=%v\n",*ptr) //如果想要输出存放的内存地址的值,要在变量前面加星号(*)

}

返回

0xc00000a098
Ptr=0xc00000a098
Ptr=10

我们可以看到,指针变量ptr的值,存放的就是变量i的内存地址。也就是说在ptr指向了变量i的内存地址

 

 说明

上面我们通过 &i 得到了i变量的内存空间的地址是0xc00000a098,ptr的变量的值实际上是i变量的内存空间的值,

如图所知,既然i变量的值是10,内存空间是0xc00000a098,ptr变量的值是0xc00000a098,ptr也是一个变量

那么ptr变量所存放值的地方,是不是也会有一个内存空间呢?是的,ptr这个指针变量也会指向一个内存空间

 案例

package main

import "fmt"

func main(){
	var i int = 10
	fmt.Println("i的地址=",&i)

	var ptr *int = &i
	fmt.Printf("Ptr=%v\n",ptr)

	//同样的道理,他也可以用地址符去取
	fmt.Printf("ptr 指针变量的内存地址=%v",&ptr)

}

返回

i的地址= 0xc00000a098
Ptr=0xc00000a098
ptr 指针变量的内存地址=0xc0000ce020

 我们再套个娃,这里去获取指针变量ptr的值

package main

import "fmt"

func main(){
	var i int = 10
	fmt.Println("i的地址=",&i)

	var ptr *int = &i
	fmt.Printf("Ptr存储内存地址的值=%v\n",ptr)        //输出ptr指针变量的值    变量i的内存地址
	fmt.Printf("ptr存储内存地址指向的值=%v\n",*ptr)   //获取ptr的值

	fmt.Printf("ptr自身指针变量的内存地址=%v\n",&ptr)  //同样的道理,他也可以用地址符去取,输出ptr的内存地址

	fmt.Printf("变量i的值=%v\n",**&ptr)  
                   //通过调用ptr指针变量内存地址(&ptr),去调用ptr指针变量存储值(*ptr),
	               //上面获取的还是内存地址,所以要再一次*获取真正的值所以是**&ptr
	               //每多一层指针,调用加一层*

}

小练习

1、写一个程序,获取一个变量num的地址,并显示到终端
2.、将num的地址赋给指针ptr,并通过ptr去修改num的值

package main

import "fmt"

func  main()  {
	num := 10
	fmt.Println(&num)

	var ptr *int = &num

	fmt.Println(*ptr)
	fmt.Println(ptr)

	*ptr = 20            //修改指针变量指定的内存空间的值, 基于地址的修改会影响原先的值
	fmt.Println(num)     //查看原始值
	fmt.Println(*ptr)    //查看指针变量指定的内存空间的值
}

错误案例

var ptr *int = a    //错误,指针变量是要接收一个内存地址的。这里给了一个值,所以会出错 

var ptr *float32 = &a  //错误 指针的类型必须要和接收的地址的类型必须相同

练习2

package main

import "fmt"

func main(){
	var a int = 300
	var b int = 400

	var ptr *int

	ptr = &a
	*ptr = 100

	ptr = &b
	*ptr = 200

	fmt.Printf("a=%d,b=%d,ptr=%d",a,b,*ptr)
}

//猜猜以上代码会输出什么
//100,200,200

指针细节说明

1. 值类型,都有对应的指针类型,形式为 *数据类型
           比如int的对应的指针就是*int,  float32对应的指针类型就是 *float32,以此类推

2. 值类型包括
   基本数据类型int系列
   float系列
   bool
   string
   数组
   结构体

 

三 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil, nil 指针也称为空指针

nil 在概念上和其它语言的 null 、 None 、 nil 、 NULL 一样,都指代零值或空值  、空指针值为 nil,即没有内存地

 案例

package main

import "fmt"

func main(){
	var intP *int 
	fmt.Println(*intP)     
}

返回

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xa7c516]

这时候该怎么办呢?其实只需要通过 new 函数给它分配一块内存就可以了,如下

package main

import "fmt"

func main(){
	var intP *int = new(int)   //new(int) 分配一块内存空间,数据类型是int
	fmt.Println(*intP)      //0
}

案例 空指针输出

package main

import "fmt"

func main() {
	var  ptr *int

	fmt.Printf("ptr 的值为 : %x\n", ptr  )

	//或者进行判空操作
	if(ptr != nil){
		fmt.Println("内存地址为",ptr)
	}else {
		fmt.Println("ptr 为空指针")
	}
}