复杂数据类型-指针
Go语言指针详解,看这一篇文章就够了 (opens new window)
# 指针介绍
Go语言为程序员提供了控制数据结构 (opens new window)指针的能力,但是,并不能进行指针运算。Go语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式,这对于构建运行良好的系统是非常重要的。指针对于性能的影响不言而喻,如果你想要做系统编程、操作系统或者网络应用,指针更是不可或缺的一部分。
指针(pointer)在Go语言中可以被拆分为两个核心概念:
- 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
- 切片,由指向起始元素的原始指针、元素数量和容量组成。(切片比原始指针具备更强大的特性,而且更为安全。切片在发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。)
要明白指针,需要知道几个概念:指针地址、指针类型和指针取值,下面将展开详细说明。
基本数据类型(又叫值类型),都有对应的指针类型,形式为*数据类型
# 指针地址、指针类型、指针取值
**指针地址:**一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节,占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时,它的默认值为 nil。指针变量通常缩写为 ptr。
指针类型:每一个定义的指针都有一个类型,这个类型可以根据赋值默认指定
指针取值:取出指针地址中的值
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置Go语言中使用在变量名前面添加&
操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:
ptr := &v // v 的类型为 T
其中 v 代表被取地址的变量,变量 v 的地址使用变量 ptr 进行接收,ptr 的类型为*T,称做 T 的指针类型,*代表指针。
package main
import "fmt"
func main() {
// 基本数据类型
var age int = 18
fmt.Printf("age类型:%T,值:%d,内存地址:%d\\n ", age, age, &age)
var par *int = &age
fmt.Printf("par指针类型:%T,赋值age的内存地址:%d,指针地址:%d,指针取值:%d\\n ", par, par, &par, *par)
}
/*
age类型:int,值:1,内存地址:824634908760
par指针类型:*int,赋值age的内存地址:824634277976,指针地址:824634449952,指针取值:18
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
提示:变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址。
# 定义使用指针变量
总结:最重要的就是两个符号:
- & 取内存地址
- 根据地址取值
&符号+变量 获取变量内存的地址
定义一个指针变量:
var代表要声明一个变量
ptr 指针变量的名字
ptr对应的类型是:*int 是一个指针类型 (可以理解为 指向int类型的指针)
&age就是一个地址,是ptr变量的具体的值
2
3
4
5
6
# 代码示例
package main
import "fmt"
func main() {
var age int = 18
fmt.Println("age值地址", &age) //&符号+变量 就可以获取这个变量内存的地址
//定义一个指针变量:
//var代表要声明一个变量
//ptr 指针变量的名字
//ptr对应的类型是:*int 是一个指针类型 (可以理解为 指向int类型的指针)
//&age就是一个地址,是ptr变量的具体的值
var ptr *int = &age
fmt.Println("age赋值地址:", ptr)
fmt.Println("指针地址:", &ptr)
fmt.Printf("指针类型:%T\\n", ptr)
//想获取ptr这个指针或者这个地址指向的那个数据:
fmt.Printf("指针取值:%v", *ptr) //ptr指向的数值为:18
}
/*
age值地址 0xc000120058
age赋值地址: 0xc000120058
指针地址: 0xc000144020
指针类型:*int
指针取值:18
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 指针使用注意点
# 1. 可以通过指针改变指向值
package main
import "fmt"
func main() {
// 1. 可以通过指针改变指向值
var num int = 10
fmt.Println("初始值", num)
var ptr *int = &num
*ptr = 20
fmt.Println("修改后", num)
}
初始值 10
修改后 20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2. 指针变量接收的一定是地址值
package main
import "fmt"
func main() {
// 2. 指针变量接收的一定是地址值
var num int = 10
var ptr1 *int = num // cannot use num (variable of type int) as type *int in variable declaration
}
2
3
4
5
6
7
8
9
# 3. 指针变量的地址不可以不匹配
package main
import "fmt"
func main() {
// 3. 指针变量的地址不可以不匹配
var num int = 10
var par2 *float32 = &num // cannot use &num (value of type *int) as type *float32 in variable declaration
}
2
3
4
5
6
7
8
9
*float32意味着这个指针指向的是float32类型的数据,但是&num对应的是int类型的不可以。
# 4. !基本数据类型(又叫值类型),都有对应的指针类型,形式为*数据类型
比如int的对应的指针就是int, float32对应的指针类型就是float32。依次类推。
# 创建指针的另一种方法——new() 函数
Go语言还提供了另外一种方法来创建指针变量,格式如下:
new(类型)
package main
import "fmt"
func main() {
str := new(string) // 先创建一个内存地址
int1 := new(int) // 先创建一个内存地址
fmt.Println(str)
fmt.Println(int1)
*str = "Go语言教程" // 在往这个指针类型赋值
fmt.Println(*str)
}
/*
0xc000050250
0xc00001e0a8
Go语言教程
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。