09、Golang 教程 - Go 指针

  • Go 中指针不像 C 语言里的那么难理解,使用指针可以更简单的执行一些任务。
  • 变量是一种使用方便的占位符,用于引用计算机内存地址。
  • 取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。
  • 和 & 的区别:

  • & 是取地址符号,即取得某个变量的地址 , 如 &a。

    • 是指针运算符,可以表示一个变量是指针类型,也可以表示一个指针变量所指向的存储单元,也就是这个地址所存储的值。

1. 指针概念

一个指针变量指向了一个值的内存地址。

类似于变量和常量,在使用指针前需要声明指针。指针声明格式如下:

var varName *varType

varType 是指针类型,varName 为指针变量名,* 号用于指定变量是作为一个指针。

var ip *int			// 指向整型
var fp *float32		// 指向浮点型

 

2. 使用指针流程

  • 定义指针变量。
  • 为指针变量赋值。
  • 访问指针变量中指向地址的值。

在指针类型前面加上 * 号来获取指针指向的内容。

示例:

package main

import "fmt"

func main() {
   
     
    var (
        a int = 33 //实际变量
        ip *int	   //指针变量
    )
    
    // a 的地址赋值给 ip 指针
    ip = &a
    
    fmt.Println("a 的值:",a)
    fmt.Println("a 的地址:",&a)
    fmt.Println("指针 ip 的值:",ip)
    fmt.Println("指针 ip 指向的值:",*ip)
}

// 结果
a 的值: 33
a 的地址: 0xc00000a0a0
指针 ip 的值: 0xc00000a0a0
指针 ip 指向的值: 33

3. 空指针

  • 当一个指针被定义后没有分配任何变量时,它的值为 nil。
  • nil 指针也被称为空指针。
  • nil 在概念上和其它语言的 null、None、nil、NULL 一样,都指代零值或空值。
  • 一个指针变量通常缩写为 ptr。
package main

import "fmt"

func main() {
   
     
   var ptr *int

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

// 结果
ptr 的值为 : 0

空指针判断:

if(ptr != nil)    /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

4. 指针数组

package main

import "fmt"

func main() {
   
     
   a := []int{
   
     10,100,200}

   for i := 0; i < len(a); i++ {
   
     
      fmt.Printf("a[%d] = %d\n", i, a[i] )
   }
}

// 结果
a[0] = 10
a[1] = 100
a[2] = 200

如果我们需要保存数组,这样我们就需要使用到指针。

声明整型指针数组:

var ptr [num]*int

ptr为整型指针数组。因此每个元素都指向了一个值。以下实例的三个整数将存储在指针数组中:

package main

import "fmt"

func main() {
   
     
	a := []int{
   
     10,100,200}
	var ptr [3]*int		//声明指针数组

	for  i := 0; i < 3; i++ {
   
     
		ptr[i] = &a[i]		//整数地址赋值给指针数组
	}

	for  i := 0; i < 3; i++ {
   
     
		fmt.Printf("a[%d] = %d\n", i,*ptr[i] )
	}
}

// 结果
a[0] = 10
a[1] = 100
a[2] = 200

综合示例

package main

import "fmt"

func main() {
   
     
	var ptr [3]*int	//声明指针数组
	a := []int{
   
     10,100,200}	//实际数组

	for i:=0;i<len(a);i++ {
   
     
		//地址赋值给指针
		ptr[i] = &a[i]
		fmt.Printf("第 %d 个元素指针地址为 %d\n",i,&a[i])
	}

	//使用指针变量指向值进行遍历
	for j:=0;j<len(ptr);j++ {
   
     
		fmt.Printf("a[%d] = %d\n",j,*ptr[j])
	}

	// 实际数组的遍历
	/*
	   for i:=0;i<len(a);i++ {
	       fmt.Printf("a[%d] = %d\n",i,a[i])
	   }
	*/
}

// 结果
第 0 个元素指针地址为 824633795424
第 1 个元素指针地址为 824633795432
第 2 个元素指针地址为 824633795440
a[0] = 10
a[1] = 100
a[2] = 200

5. 指向指针的指针

  • 如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
  • 当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址:
     
  • 指向指针的指针变量声明格式如下:
var ptr **int;
// 以上指向指针的指针变量为整型

  • 访问指向指针的指针变量值需要使用两个 * 号

示例一

package main

import "fmt"

func main() {
   
     
	var (
		a int			//变量
		ptr *int		//指针
		pptr **int		//指针的指针
	)
	//变量赋值
	a = 3000

	//ptr 赋值 a 的地址
	ptr = &a

	//pptr 赋值 ptr 的地址
	pptr = &ptr

	fmt.Println("a 的值:",a)
	fmt.Println("a 的地址:",ptr)
	fmt.Println("指针 ptr 指向的值:",*ptr)
	fmt.Println("指针 pptr 指向的值:",*pptr)
	fmt.Println("指针 pptr 指向指针 ptr 指向的值:",**pptr)
	fmt.Println("指针 pptr 的地址:",&pptr)
}

// 结果
a 的值: 3000
a 的地址: 0xc00000a0a0
指针 ptr 指向的值: 3000
指针 pptr 指向的值: 0xc00000a0a0
指针 pptr 指向指针 ptr 指向的值: 3000
指针 pptr 的地址: 0xc000006030

 
示例二

package main

import "fmt"

func main() {
   
     
	var a int = 5
	var ptr *int = &a	//一级指针,指向 a 的地址,可获取 a 的值
	var pts *int = ptr	//一级指针,指向 ptr 指向的地方,可获得 a 的地址,以及 a 的值
	var pto **int = &ptr	//二级指针,指向 ptr 地址,ptr 指向 &a 的地址,所以 pto 可以获得 ptr 地址,可获得 a 的地址,可获得 a 的值
	var pt3 ***int = &pto 	//三级指针,指向 pto 地址,pto 指向 ptr,ptr 指向 a,可获得 pto 地址,获得 ptr 地址,可获得 a 地址,获得 a 的值

	fmt.Println("a 的地址为:",&a)
	fmt.Println("a 的值为:",a)

	fmt.Println("ptr 的地址为:",&ptr)
	fmt.Println("ptr 指向的地址为:",ptr)

	fmt.Println("pts 的地址为:",&pts)
	fmt.Println("pts 指向的地址为:",pts)
	fmt.Println("pts 指向的地址对应的值为:",*pts)

	fmt.Println("pto 的地址为:",&pto)
	fmt.Println("pto 指向的指针(ptr)的存储地址为:",pto)
	fmt.Println("pto 指向的指针(ptr)所指向的地址为:",*pto)
	fmt.Println("pto 最终指向的地址对应的值(a)为:",**pto)

	fmt.Println("pt3 的地址为:",&pt3)
	fmt.Println("pt3 指向的指针(pto)的存储地址为:",pt3)
	fmt.Println("pt3 指向的指针(pt0)所指向的地址为:",*pt3)
	fmt.Println("pt3 最终指向的地址对应的值(a)为:",***pt3)
}

// 结果
a 的地址为: 0xc0000a2058
a 的值为: 5
ptr 的地址为: 0xc0000cc018
ptr 指向的地址为: 0xc0000a2058
pts 的地址为: 0xc0000cc020
pts 指向的地址为: 0xc0000a2058
pts 指向的地址对应的值为: 5
pto 的地址为: 0xc0000cc028
pto 指向的指针(ptr)的存储地址为: 0xc0000cc018
pto 指向的指针(ptr)所指向的地址为: 0xc0000a2058
pto 最终指向的地址对应的值(a)为: 5
pt3 的地址为: 0xc0000cc030
pt3 指向的指针(pto)的存储地址为: 0xc0000cc028
pt3 指向的指针(pt0)所指向的地址为: 0xc0000cc018
pt3 最终指向的地址对应的值(a)为: 5

6. 指针作为函数参数

Go 语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可。

// 向函数传递指针,并在函数调用后修改函数内的值
package main

import (
	"fmt"
)

func main() {
   
     
	// 定义局部变量
	var a int = 100
	var b int = 200
	fmt.Println("原 a 值:", a)
	fmt.Println("原 b 值:", b)

	/*
		调用 swap 函数用于交换值
		&a 指向 a 变量的地址
		&b 指向 b 变量的地址
	*/
	swap(&a, &b)
	fmt.Println("交换后 a 值:", a)
	fmt.Println("交换后 b 值:", b)
}

// 定义交换函数
func swap(x *int, y *int) {
   
     
	var temp int
	temp = *x
	*x = *y
	*y = temp
}

/* 交换函数也可以这样写,仅限于 go 语言
func swap(x *int, y *int) {
	*x, *y = *y, *x
}
*/

// 结果
原 a 值: 100
原 b 值: 200
交换后 a 值: 200
交换后 b 值: 100