Go语言指针切片(存储指针的切片)
切片是一种比较特殊的数据结构,这种数据结构更便于使用和管理数据集合。切片是围绕动态数组的概念构建的,可以按需自动增长和缩小,总的来说,切片可理解为动态数组,并根据切片里的元素自动调整切片长度。
Go 语言的切片指针是以切片表示的,切片的每个元素只能存放内存地址,切片指针的语法定义如下:
切片指针的定义与切片定义是相同的,只要在数据类型前面使用符号“*”即可变为切片指针。由于切片有多种不同的定义方式,因此切片指针也会有多种定义方式,这里只列举了常用的定义方式。
切片指针可以将多个变量的内存地址存放在切片中,这样方便管理多个变量,当需要修改某个变量的时候,由于变量的内存地址是不会改变的,直接修改变量或者从切片指针修改变量即可,修改后的数据都会同步到变量和切片指针中,示例如下:
如果不掌握切片指针的基本原理,在实际开发中程序很容易埋下难以寻找的 bug,示例如下:
如果要修改上述问题,只能在第一次 for 循环中重新定义变量 a,每次循环为变量 a 重新赋予新的内存地址,代码如下:
Go 语言的切片指针是以切片表示的,切片的每个元素只能存放内存地址,切片指针的语法定义如下:
// 定义方式一 var name []*type // 定义方式二 name := []*type{}语法说明如下:
- name代表指针变量名,可自行命名,但必须遵从标识符命名规则。
- type是指针变量的数据类型,如数字、字符串等 Go 语言内置的数据类型。
切片指针的定义与切片定义是相同的,只要在数据类型前面使用符号“*”即可变为切片指针。由于切片有多种不同的定义方式,因此切片指针也会有多种定义方式,这里只列举了常用的定义方式。
切片指针可以将多个变量的内存地址存放在切片中,这样方便管理多个变量,当需要修改某个变量的时候,由于变量的内存地址是不会改变的,直接修改变量或者从切片指针修改变量即可,修改后的数据都会同步到变量和切片指针中,示例如下:
package main import "fmt" func main() { // 定义一个空的字符串类型的切片指针 var pslice []*string fmt.Printf("切片指针的元素:%v,内存地址:%v\n", pslice, &pslice) // 定义变量a、b、c并赋值 var a, b string a, b = "a", "b" fmt.Printf("变量a、b的内存地址:%v、%v\n", &a, &b) // 使用内置函数方法append()将变量a、b、c的内存地址添加到切片指针 pslice = append(pslice, &a) pslice = append(pslice, &b) fmt.Printf("切片指针的元素:%v\n", pslice) // 输出切片指针的元素所对应的数值 // 使用取值操作符“*”从内存地址取值 for _, k := range pslice{ fmt.Printf("切片指针的元素所对应值:%v\n", *k) } // 从切片指针修改变量a的值,输出变量a *pslice[0] = "hello" fmt.Printf("修改后的变量值为:%v\n", a) // 修改变量b的值,输出切片指针的变量b的值 b = "Golang" fmt.Printf("修改后的变量值为:%v\n", *pslice[1]) }运行上述代码,运行结果为:
切片指针的元素:[],内存地址:0xc00000e028
变量a、b的内存地址:0xc000010200、0xc000010210
切片指针的元素:[0xc000010200 0xc000010210]
切片指针的元素所对应值:a
切片指针的元素所对应值:b
修改后的变量值为:hello
修改后的变量值为:Golang
- 切片指针定义后,如果没有设置初始值,默认为空,由于切片是动态数组,其数据长度能自动调整,Go 语言不会分配内存地址,因此无法通过取地址操作符“&”获取切片指针的内存地址。
- 若将变量 a、b 写入切片指针,只能将变量 a、b 的内存地址写入切片指针,切片指针只能存放内存地址的数据格式。
- 使用 for-range 循环输出切片指针,只能输出存放在切片指针的内存地址,如果要通过内存地址获取对应数值,需要使用取值操作符“*”。
- 修改变量的值不会改变变量的内存地址,所以修改变量 a 或变量 b 的值,再从切片指针中获取变量 a 或变量 b 的值,输出结果都是变量 a 或变量 b 修改后的数值。同理,如果从切片指针中修改变量 a 或变量 b 的值,输出的变量 a 或变量 b 的值都是修改后的数值。
如果不掌握切片指针的基本原理,在实际开发中程序很容易埋下难以寻找的 bug,示例如下:
package main import ( "fmt" "strconv" ) func main() { // 定义一个空的字符串类型的切片指针 var pslice []*string // 定义字符串类型的变量a var a string // 循环5次,当前循环次数赋值给变量a,再写入切片指针 for i := 0; i < 5; i++ { a = strconv.Itoa(i) pslice = append(pslice, &a) } // 输出切片指针的元素的数值 for _, k := range pslice { fmt.Printf("切片指针的元素:%v,元素的值:%v\n", k, *k) } }分析上述代码,它定义了变量 a 和切片指针 pslice,再执行了两次 for 循环。第一次 for 循环是将每次循环次数赋值给变量 a,然后将变量 a 的内存地址写入切片指针 pslice;第二次 for 循环是输出切片指针 pslice 的元素和元素值,运行结果为:
切片指针的元素:0xc000010200,元素的值:4
切片指针的元素:0xc000010200,元素的值:4
切片指针的元素:0xc000010200,元素的值:4
切片指针的元素:0xc000010200,元素的值:4
切片指针的元素:0xc000010200,元素的值:4
如果要修改上述问题,只能在第一次 for 循环中重新定义变量 a,每次循环为变量 a 重新赋予新的内存地址,代码如下:
package main import ( "fmt" "strconv" ) func main() { // 定义一个空的字符串类型的切片指针 var pslice []*string // 循环5次,当前循环次数赋值给变量a,再写入切片指针 for i := 0; i < 5; i++ { // 定义字符串类型的变量a var a string a = strconv.Itoa(i) pslice = append(pslice, &a) } // 输出切片指针的元素的数值 for _, k := range pslice { fmt.Printf("切片指针的元素:%v,元素的值:%v\n", k, *k) } }运行上述代码,运行结果为:
切片指针的元素:0xc000010200,元素的值:0
切片指针的元素:0xc000010210,元素的值:1
切片指针的元素:0xc000010220,元素的值:2
切片指针的元素:0xc000010230,元素的值:3
切片指针的元素:0xc000010240,元素的值:4