指针
# 9. 指针
# 9.1 关于指针
要搞明白 Go 语言中的指针需要先知道 3 个概念: ==指针地址、指针类型、指针取值==
- 指针地址(&a)
- 指针取值(*&a)
- 指针类型(&a) —> *int 改变数据传指针
变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量 a := 10 ,这个时候可以直接通过 a 这个变量来读取内存中保存的 10 这个值。在计算机底层 a 这个变量其实对应了一个内存地址。指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址。
Go 语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和 *(根据地址取值)
package main
import "fmt"
func main() {
var a = 10
fmt.Printf("%d \n", &a) // &a 指针地址 (824633802904)
fmt.Printf("%d \n", *&a) // *&a 指针取值 (10)
fmt.Printf("%T \n", &a) // %T 指针类型 (*int )
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 9.2 &取变量地址
&符号取地址操作
package main
import "fmt"
func main() {
var a = 10
var b = &a
var c = *&a
fmt.Println(a) // 10 a的值
fmt.Println(b) // 0xc000014098 a变量的内存地址
fmt.Println(c) // 10 *内存地址 取值
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
b := &a 图示:
# 9.3 new和make
package main
import "fmt"
func main() {
var userInfo map[string]string
userInfo["username"] = "zhangsan"
fmt.Println(userInfo)
}
/*
panic: assignment to entry in nil map
*/
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
执行上面的代码会引发 panic,为什么呢?
在 Go 语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。
而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。
要分配内存,就引出来今天的 new 和 make。Go 语言中 new 和 make 是内建的两个函数,主要用来分配内存。
# 9.3.1 make和new比较
make和new的区别:
- make 关键字的作用是创建于 slice、map 和 channel 等内置的数据结构;
- new 关键字的作用是为类型申请一片内存空间,并返回指向这片内存的指针。
package main
import "fmt"
func main() {
a := make([]int, 3, 10) // 切片长度为 1,预留空间长度为 10
a = append(a, 1)
fmt.Printf("%v--%T \n", a, a) // [0 0 0 1]--[]int 值----切片本身
var b = new([]int)
// b = b.append(b,2) // 返回的是内存指针,所以不能直接 append
*b = append(*b, 2) // 必须通过 * 指针取值,才能进行 append 添加
fmt.Printf("%v--%T \n", b, b) // &[2]--*[]string 内存的指针---内存指针
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 9.3.2 new函数
系统默认的数据类型,分配空间
package main
import "fmt"
func main() {
// 1.new实例化int
age := new(int)
*age = 1
fmt.Println(*age) // 1
// 2. new实例化切片
li := new([]int)
*li = append(*li, 1)
fmt.Println(*li) // [1]
// 3. new实例化map
userInfo := new(map[string]string)
*userInfo = map[string]string{}
(*userInfo)["username"] = "张三"
fmt.Println(userInfo) // &map[username:张三]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
自定义类型使用 new 函数来分配空间
package main
import "fmt"
func main() {
var s *Student
s = new(Student) //分配空间
s.name = "zhangsan"
fmt.Println(s) // &{zhangsan 0}
}
type Student struct {
name string
age int
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 9.3.3 make函数
make 也是用于内存分配的,但是和 new 不同,它只用于 chan、map 以及 slice 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
package main
import "fmt"
func main() {
a := make([]int, 3, 10) // 切片长度为 1,预留空间长度为 10
b := make(map[string]string)
c := make(chan int, 1)
fmt.Println(a, b, c) // [0 0 0] map[] 0xc0000180e0
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
当为slice分配内存的时候,应当尽量预估到slice可能的最大长度,通过给make传第三个参数的方式来给slice预留好内存空间,这样可以避免二次分配内存带来的开销,大大提高程序的性能。
上次更新: 2022/06/12, 15:48:09