【10.0】Go语言基础之指针

发布时间 2023-11-12 12:04:11作者: Chimengmeng

【一】什么是指针

  • 指针是—种存储变量内存地址(Memory Address)的变量。

  • 如上图所示,变量b 的值为156 ,而b 的内存地址为0x1040a124。

    • 变量 α存储了b 的地址。
    • 我们就称a指向了b 。

【二】指针的定义

【1】指针的语法基础

  • 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
  • 2 在变量前加 & 取地址符号,表示取该变量的地址
  • 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

【2】指针的定义和使用

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// [1]定义一个int类型的变量
	var a int = 10

	// [2]定义一个变量,来存a的地址 : * 表示指针类型
	var p *int

	// [3]取地址 :& 表示取该变量的地址
	p = &a

	// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
	// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
	fmt.Println("当前指针 p :>>>> ", p)
	// 当前指针 p :>>>>  0xc00001e0a8
	// 当前指针 p :>>>>  0xc0000a6058
}

【3】指针的指针

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// [1]定义一个int类型的变量
	var a int = 10

	// [2]定义一个变量,来存a的地址 : * 表示指针类型
	var p *int

	// [3]取地址 :& 表示取该变量的地址
	p = &a

	// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
	// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
	fmt.Println("当前指针 p :>>>> ", p)
	// 当前指针 p :>>>>  0xc00001e0a8
	// 当前指针 p :>>>>  0xc0000a6058

	// [4]指针的指针
	var p1 **int = &p // 取一次加一个 *
	fmt.Println("当前指针 p1 :>>>> ", p1)
	var p2 ***int = &p1
	fmt.Println("当前指针 p2 :>>>> ", p2)

	//当前指针 p :>>>>  0xc00001e0a8
	//当前指针 p1 :>>>>  0xc00000a028
	//当前指针 p2 :>>>>  0xc00000a038
}

【4】指针反解

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// [1]定义一个int类型的变量
	var a int = 10

	// [2]定义一个变量,来存a的地址 : * 表示指针类型
	var p *int

	// [3]取地址 :& 表示取该变量的地址
	p = &a

	// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
	// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
	fmt.Println("当前指针 p :>>>> ", p)
	// 当前指针 p :>>>>  0xc00001e0a8
	// 当前指针 p :>>>>  0xc0000a6058

	// [4]指针的指针
	var p1 **int = &p // 取一次加一个 *
	fmt.Println("当前指针 p1 :>>>> ", p1)
	var p2 ***int = &p1
	fmt.Println("当前指针 p2 :>>>> ", p2)

	//当前指针 p :>>>>  0xc00001e0a8
	//当前指针 p1 :>>>>  0xc00000a028
	//当前指针 p2 :>>>>  0xc00000a038

	// [5]地址反解成值 : 在(指针)变量前加 * 表示反解,把地址解析成具体的值
	fmt.Println("反解*p :>>>> ", *p)       // 反解指针变量 p 指向 a 解析出 a 的值
	fmt.Println("反解*p1 :>>>> ", *p1)     // 反解指针变量 p1 指向 p 解析出 p 的值
	fmt.Println("反解**p1 :>>>> ", **p1)   // 反解指针变量 p1 指向 p 再指向 a 解析出 a 的值
	fmt.Println("反解*p2 :>>>> ", *p2)     // 反解指针变量 p2 指向 p1 解析出 p1 的值
	fmt.Println("反解***p2 :>>>> ", ***p2) // 反解指针变量 p2 指向 p1 指向 p 再指向 a 解析出 a 的值

	//当前指针 p :>>>>  0xc00001e0a8
	//当前指针 p1 :>>>>  0xc00000a028
	//当前指针 p2 :>>>>  0xc00000a038
	//反解*p :>>>>  10
	//反解*p1 :>>>>  0xc00001e0a8
	//反解**p1 :>>>>  10
	//反解*p2 :>>>>  0xc00000a028
	//反解***p2 :>>>>  10
}

【5】指针类型的零值

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// [5] 指针的零值
	name := "Dream"
	var n *string

	fmt.Println("当前指针指向变量前 n :>>>> ", n)
	// 指针指向 变量 name 取值 &
	n = &name
	fmt.Println("当前指针指向变量后 n :>>>> ", n)

	//当前指针指向变量前 n :>>>>  <nil>
	//当前指针指向变量后 n :>>>>  0xc000088240
}

【6】指针是引用类型

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// 指针是引用类型 ----> 当参数传递
	a := 10 // var a int = 10
	p := &a // var p *int = &a

	// 调用函数
	foo(p)
	fmt.Println("函数调用后修改值 :>>>> ", *p)

	//反解指针类型 p :>>>>  10
	//函数内部修改值 :>>>>  99
	//函数调用后修改值 :>>>>  99
}

// 参数类型为 *int : 指针类型(指向 int 类型的指针)
func foo(p *int) {
	fmt.Println("反解指针类型 p :>>>> ", *p)

	// 修改值 : 并不是修改 p 的值,而是修改 p 指向的 a 的值
	*p = 99
	fmt.Println("函数内部修改值 :>>>> ", *p)
}

【7】不要向函数传递数组的指针,而应该使用切片

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// 不要向函数传递数组的指针,而应该使用切片
	var name = [3]string{"梦梦", "蚩梦", "小梦"}

	// 调用函数 : 不要向函数传递数组的指针
	foo(&name) // 把 数组 name 的地址取出来 ---> 变成了 指针类型指向 name
	fmt.Println("函数外修改后的数组 :>>>> ", name)

	//取值 p[1] :>>>>  蚩梦
	//函数内修改后的数组 :>>>>  [醉梦 蚩梦 小梦]
	//函数外修改后的数组 :>>>>  [醉梦 蚩梦 小梦]

	var a []string = name[:]
	// 调用函数 : 传切片进去
	bar(a)
	fmt.Println("函数外修改后的切片 :>>>> ", a)
	fmt.Println("函数外修改后的数组 :>>>> ", name)

	//取值 o[1] :>>>>  蚩梦
	//函数内修改后的数组 :>>>>  [晓梦 蚩梦 小梦]
	//函数外修改后的切片 :>>>>  [晓梦 蚩梦 小梦]
	//函数外修改后的数组 :>>>>  [晓梦 蚩梦 小梦]

	// 总结 :
	//	使用切片的好处是,如果数组长度变了,函数不需要重写
	//	如果是数组,需要重写再写一个函数对应
}

// 参数类型为 *int : 指针类型(指向 int 类型的指针)
func foo(p *[3]string) {
	// (*p)[1] ----> 先对 p 解引用 变成数组, 再对 p 取值
	fmt.Println("取值 p[1] :>>>> ", (*p)[1])
	// 修改值
	(*p)[0] = "醉梦"
	fmt.Println("函数内修改后的数组 :>>>> ", *p)
}

// 参数类型为 o []string : 切片类型
func bar(o []string) {

	// (*p)[1] ----> 先对 p 解引用 变成数组, 再对 p 取值
	fmt.Println("取值 o[1] :>>>> ", o[1])
	// 修改值
	o[0] = "晓梦"
	fmt.Println("函数内修改后的数组 :>>>> ", o)
}

【8】数组的指针不需要解引用

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// 如果是数组的指针,不需要解引用,直接使用即可,按索引取值即可
	var a = [3]int{7, 8, 9}
	var p *[3]int = &a

	fmt.Println("解引用再取值 :>>>> ", (*p)[0])
	fmt.Println("不需要解引用再取值 :>>>> ", p[0]) // 支持直接按索引取值
	fmt.Println("取 p  :>>>> ", p)         // &[7 8 9]

	//解引用再取值 :>>>>  7
	//不需要解引用再取值 :>>>>  7
	//取 p  :>>>>  &[7 8 9]
}

【9】不支持指针运算

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// go 语言不支持指针运算
	var a = [3]int{7, 8, 9}
	var p *[3]int = &a

	// p++ : 在 c 语言中表示 p[1]
	fmt.Println("指针运算 :>>>> ", p++) // ')', ',' or '...' expected, got '++'
}

【10】指针数组和数组指针

package main

import "fmt"

func main() {
	// 指针 :存储 变量内存地址  的  变量

	// 1 类型前放 *  表示指针类型,这个类型的指针,指向这个类型的指针
	// 2 在变量前加 & 取地址符号,表示取该变量的地址
	// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值

	// 指针数组(数组里放了一堆指针)和数组指针(指向数组的指针)

	// 定义数组
	var a = [3]int{7, 8, 9}

	// 定义指针 指向数组 --- 数组指针
	var p *[3]int = &a
	fmt.Println("这是定义的数组指针 p :>>>> ", p)
	// 这是定义的数组指针 p :>>>>  &[7 8 9]

	// 定义数组 , 数组内的类型 为 指针类型 ----> 指针数组
	var x, y, z = 4, 5, 6
	var b [3]*int = [3]*int{&x, &y, &z}
	fmt.Println("这是定义的指针数组  b :>>>> ", b)
	// 这是定义的指针数组  b :>>>>  [0xc0000a6090 0xc0000a6098 0xc0000a60a0]
}