Go 快速开发 | 07 - Go 中的指针

165 阅读4分钟

一、指针

指针的定义

对于引用数据类型来说,变量指向的不是数据值本身,而是指向数据值本身所在的内存空间的地址,而指针就是用来存储内存地址且只能是内存地址的一个变量。

变量的本质就是给存储数据的内存地址起一个更通俗易懂的别名。

// 定义指针变量的方式
var 变量名 *变量数据类型
func main() {

   var a = 1
   
   // 定义一个指针变量,指针变量存储 int 数据类型
   var zulu *int = &a

   fmt.Printf("%T\n", zulu)
   fmt.Printf("%v\n", zulu)
   fmt.Printf("%v\n", *zulu)

}

执行上述代码,输出结果如下:

*int
0xc0000b2008
1

上述代码中 *int 就是指针的类型,指针变量指向内存地址中所存储的值可以通过 *指针变量名来获取,当然也可以不通过定义指针变量的方式而是直接通过 &变量名 获取内存地址或者通过 *&变量名 来获取内存地址所指向的值。

上面讲到过指针变量只能存储地址,也就是说定义指针变量的时候是不能直接赋值的,一定要给一个内存地址,而 &变量名 可以获取内存地址,上述代码就是将 a 变量的内存地址赋值赋值给指针 zulu,如果直接赋值会报错:

image.png

另外指针定义时,指针变量指向的类型(如*int)必须和存储地址中存储的数据类型必须一致(也必须存储 int),否则会报错:

image.png

指针有多种数据类型,指针指向的类型必须和内存地址对应的数据类型一致。指针指向的内存地址可以通过 & 获取,内存地址指向的值可以通过 *&a 获取。

image.png

make 函数和 new 函数

// filename: ex5.go

func main() {

   var info map[string]string

   info["name"] = "tony"

   fmt.Println(info)
}

执行上述代码,输出结果如下:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/ex5.go:9 +0x32

这里执行报错是因为没有初始化,Go 语言中对于引用数据类型使用的时候不仅要声明它,还要分配内存空间,否则赋值的时候就无法存储。对于值类型的声明不需要分配内存空间,因为在声明的时候就已经默认分配好了内存地址。

Go 中分配内存需要使用到两个内置函数 make 函数和 new 函数,其中:

  • make 函数用于创建切片 slice、字典 map 和 管道 channel 等该数据类型时分配内存空间
    • 只能对内置数据类型进行申请内存空间
    • 返回数据值本身
  • new 函数的作用是为类型申请一片内存空间,并返回指向这块内存空间的内存地址(返回一个指针)
    • new 方法主要是给 struct 一类非内置数据类型申请内存空间
    • 返回一个指针
func main() {

   var info = make(map[string]string)

   info["name"] = "tony"

   fmt.Println(info)
   
}

执行上述代码,输出结果如下:

map[name:tony]

使用 make 函数给字典 info 分配内存空间。

func main() {
   
   // 分配内存,返回内存地址
   sli := new([]int)
   // *sli 表示切片的值
   *sli = append(*sli, 1)
   fmt.Printf("%T, %T\n", sli, *sli)

   // 分配内存,返回内存地址
   infoPoi := new(map[string]string)
   // 内存地址指向的值赋值给info
   info := *infoPoi
   // 初始化 info
   info = map[string]string{}
   info["name"] = "zhangsan"
   fmt.Printf("%v, %T, %v, %T\n", infoPoi, infoPoi, info, info)

   user := make(map[string]string)
   user["username"] = "tony"
   fmt.Println(user)
   fmt.Printf("%T\n", user)

}

执行上述代码,输出结果如下:

*[]int, []int
&map[], *map[string]string, map[name:zhangsan], map[string]string
map[username:tony]
map[string]string

new 函数返回的是内存地址,既指针类型,如果需要给内存地址指向的数据赋值,需要通过 *指针变量 来获取具体的数值后进行操作。

func main() {
   // new 初始化自定义结构体
   stuPoint := new(Student)
   stu := *stuPoint
   stu.stuName = "mark"
   stu.stuAge = 12
   fmt.Printf("%v, %v, %T, %T", stu, stuPoint, stu, stuPoint)

}

type Student struct {
   stuName string
   stuAge  int
}

执行上述代码,输出结果如下:

{mark 12}, &{ 0}, main.Student, *main.Student

这里使用 new 初始化了一个自定义的结构体,关于结构体将在下面的内容中讲到,先混个眼熟。

本文正在参加技术专题18期-聊聊Go语言框架