概述
目录
- 一、指针加强
- 1 - 指针地址变量空间
- 2 - 栈帧的内存布局
- 3 - 空指针和野指针
- 4 - 函数new
- 5 - 左值与右值
- 6 - 指针的函数传参
- 二、切片加强
- 1 - 切片的基础与本质
- 2 - 切片截取、创建、append和copy
- 3 - 切片案例
- 三、map加强
- 1 - map创建与初始化
- 2 - map赋值
- 3 - map的使用
- 4 - map的删除与传参
一、指针加强
1 - 指针地址变量空间
- 指针:指针就是地址; 指针变量就是存储地址的变量
*p
:解引用、间接引用
func main() {
var a int = 10
var p *int = &a
a = 100
fmt.Println("a = ", a)
*p = 250 // 借助a 变量的地址,操作a对应空间
fmt.Println("a = ", a)
fmt.Println("*p = ", *p)
a = 1000
fmt.Println("*p = ", *p)
}
2 - 栈帧的内存布局
- 栈帧概念:用来给函数运行提供内存空间,取内存于stack上
- 当函数调用时,产生栈帧
- 函数调用结束,释放栈帧
- 栈帧存储的对象:
- 局部变量
- 形参(形参与局部变量存储地位等同)
- 内存字段描述值(栈顶指针值与栈基指针值)
3 - 空指针和野指针
- Go语言中的指针:Go语言保留了指针,但与C语言指针有所不同
- 默认值 nil
- 操作符 “&” 取变量地址, “*” 通过指针访问目标对象
- 不支持指针运算,不支持 “->” 运算符,直接 “.” 访问目标成员
- 空指针:未被初始化的指针
var p *int *p --> err
- 野指针:被一片无效的地址空间初始化
4 - 函数new
- new函数
- 表达式new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,返回的指针类型为*T
- new创建的内存空间位于heap上,空间的默认值为数据类型默认值。如:new(int) 则 *p为0,new(bool) 则 *p为false
- 我们只需使用new()函数,无需担心其内存的生命周期或怎样将其删除,因为Go语言的内存管理系统会帮我们打理一切(虽然gc会帮我们回收,我们使用完后强烈建议置成nil,以提示gc进行回收)
func main() {
var p *int
p = new (int) // 对应类型的默认值
fmt.Println(*p) // 0
}
func main() {
var p *bool
p = new(bool) // 对应类型的默认值
fmt.Println(*p) //false
}
5 - 左值与右值
- 左值:等号 左边的变量,代表 变量所指向的内存空间。 (写)
- 右值:等号 右边的变量,代表 变量内存空间存储的数据值。 (读)
var a int = 10
var b int = 20 // b是左值
a = b // b是右值
6 - 指针的函数传参
- 函数传参方式:函数传参永远是值传递(传引用传递的是地址的值)
- 传地址(引用):将形参的地址值作为函数参数传递
- 传值(数据据):将实参的 值 拷贝一份给形参
- 指针的函数传参
- 传引用:在A栈帧内部,修改B栈帧中的变量值
func swap(a, b int) {
a, b = b, a
fmt.Println("swap a:", a, "b:", b) // swap a: 20 b: 10
}
func swap2(x, y *int) {
*x, *y = *y, *x
}
func main() {
a, b := 10, 20
swap(a, b)
fmt.Println("swap: main a:", a, "b:", b) // swap: main a: 10 b: 20
swap2(&a, &b) // 传地址值
fmt.Println("swap2: main a:", a, "b:", b) // swap2: main a: 20 b: 10
}
二、切片加强
1 - 切片的基础与本质
- 为什么用切片:在Go语言当,我们几乎可以在所有的场景中,使用切片替换数组使用
- 数组的容量固定,不能自动拓展
- 值传递: 数组作为函数参数时,将整个数组值拷贝一份给形参
- 切片的本质:
- 不是一个数组的指针,是一种数据结构体,用来操作数组内部元素
- 切片并不是数组或数组指针,它通过内部指针和相关属性引用数组片段,以实现变长案
- 数组和切片定义的区别
- 创建数组时 ,[n]内指定数组长度
- 创建切片时,[]内为空,或者 …(很少使用…)
2 - 切片截取、创建、append和copy
- 切片截取:
切片名称 [ low : high : max ]
(截取数组,初始化 切片时,没有指定切片容量时, 切片容量跟随原数组(切片))- low:起始下标位置
- high:结束下标位置 len = high - low
- 容量:cap = max - low
func main() {
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s := arr[1:3:5]
fmt.Println("s = ", s)
fmt.Println("len(s) = ", len(s))
fmt.Println("cap(s) = ", cap(s))
s1 := arr[1:5:7]
fmt.Println("s1 = ", s1)
fmt.Println("len(s1) = ", len(s1)) // 5-1 == 4
fmt.Println("cap(s1) = ", cap(s1)) // 7-1
s2 := s1[0:6]
fmt.Println("s = ", s2)
fmt.Println("len(s) = ", len(s2)) // 6-0 == 6
fmt.Println("cap(s) = ", cap(s2)) // 跟随s1的容量6
}
注意:make只能创建slice、map和channel,并且返回一个有初始值(非零)的对象
- 切片创建方式
- 自动推导类型创建切片:
slice := []int {1, 2, 4, 6}
slice := make([]int, 长度,容量)
slice := make([]int, 长度)
:创建切片时,没有指定容量,容量== 长度【常用】
- 自动推导类型创建切片:
func main() {
// 1. 自动推导赋初值
s1 := []int{1, 2, 4, 6}
fmt.Println("s1 = ", s1)
s2 := make([]int, 5, 10)
fmt.Println("len=", len(s2), "cap=", cap(s2))
s3 := make([]int, 7)
fmt.Println("len=", len(s3), "cap=", cap(s3))
}
- 切片做函数参数:引用传递(传地址)
- append:在切片末尾追加元素
- append(切片对象, 待追加元素)
- append函数会智能的将底层数组的容量增长,一旦超过原底层数组容量,通常以2倍(1024以下)容量重新分配底层数组,并复制原来的数据
- 因此,使用append 给切片做扩充时,切片的地址可能发生变化。但数据都被重新保存了,不影响使用
func main() {
s1 := []int{1, 2, 4, 6} // 创建一个有初始值的切片
s1 = append(s1, 888)
s1 = append(s1, 888)
s1 = append(s1, 888)
s1 = append(s1, 888)
s1 = append(s1, 888)
fmt.Println("s1=", s1)
}
- copy:函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准,两个 slice 指向同一底层数组。直接对应位置覆盖
func main() {
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := data[8:] // {8, 9}
s2 := data[0:5] // {0, 1, 2 3 4}
copy(s2, s1)
fmt.Println("s2=", s2) // s2= [8 9 2 3 4]
}
3 - 切片案例
- 案例1:给定一个字符串列表,在原有slice上返回不包含空字符串的列表
package main
import "fmt"
func noEmpty(data []string) []string {
out := data[:0] // 在原切片上截取一个长度为 0 的切片 == make([]string, 0)
for _, str := range data {
if str != "" {
out = append(out, str)
}
// 取到空字符串,不作为
}
return out
}
// 直接在原串上操作(不适用append实现)
func noEmpty2(data []string) []string {
i := 0
for _, str := range data {
if str != "" {
data[i] = str
i++
}
// 取到空字符串,不作为
}
return data[:i]
}
func main() {
data := []string{"red", "", "black", "", "", "pink", "blue"}
afterData := noEmpty2(data)
fmt.Println("afterData:", afterData)
}
- 案例2:写一个函数,消除[]string中重复字符串
package main
import "fmt"
func noSame(data []string) []string {
out := data[:1]
// 遍历原始切片字符串
for _, word := range data {
i := 0
// 比较取出的 word 是否在 out 中存在 -- for
for ; i < len(out); i++ {
if word == out[i] {
break
}
}
if i == len(out) { //这个可以保证没有触发break的时候才执行append
out = append(out, word)
}
}
return out
}
func main() {
data := []string{"red", "black", "red", "yellow", "yellow", "pink", "blue", "pink", "blue"}
afterData := noSame(data)
fmt.Println("Afterdata:", afterData)
}
- 案例3:要删除slice中间的某个元素并保存原有的元素顺序
func remove(data []int, idx int) []int {
// {5, 6, 8, 9, 9}:将 8,9拷贝到7,8,然后丢弃掉最后一个9
copy(data[idx:], data[idx+1:])
return data[:len(data)-1]
}
func main() {
data := []int{5, 6, 7, 8, 9}
afterData := remove(data, 2)
fmt.Println("afterData:", afterData) // afterData: [5 6 8 9]
}
三、map加强
1 - map创建与初始化
- map简述:Go语言中的map(映射、字典)是一种内置的数据结构,它是一个无序的key-value对的集合,比如以身份证号作为唯一键来标识一个人的信息。Go语言中并没有提供一个set类型,但是map中的key也是不相同的,可以用map实现类似set的功能
- 在一个map里所有的键都是唯一的,而且必须是支持==和!=操作符的类型,切片、函数以及包含切片的结构类型这些类型由于具有引用语义,不能作为映射的键,使用这些类型会造成编译错误
- map创建方式
var m1 map[int]string
:不能存储数据m2 := map[int]string{}
:能存储数据m3 := make(map[int]string)
:默认len = 0- m4 := make(map[int]string, 10)
func main() {
var m1 map[int]string // 声明map ,没有空间,不能直接存储key -- value
//m1[100] = "Green"
if m1 == nil {
fmt.Println("map is nil ")
}
m2 := map[int]string{}
fmt.Println(len(m2))
fmt.Println("m2 = ", m2)
m2[4] = "red"
fmt.Println("m2 = ", m2)
m3 := make(map[int]string)
fmt.Println(len(m3))
fmt.Println("m3 = ", m3)
m3[400] = "red"
fmt.Println("m3 = ", m3)
m4 := make(map[int]string, 5) // len
fmt.Println("len(m4) = ", len(m4))
//fmt.Println("len(m4) = ", cap(m4)) // 不能在map中使用 cap()
fmt.Println("m4 = ", m4)
}
- map初始化
var m map[int]string = map[int]string{ 1: "aaa", 2:"bbb"}
:保证key彼此不重复m := map[int]string{ 1: "aaa", 2:"bbb"}
func main() {
// 初始化map
var m5 map[int]string = map[int]string{1: "Luffy", 130: "Sanji", 1301: "Zoro"}
fmt.Println("m5 = ", m5)
m6 := map[int]string{1: "Luffy", 130: "Sanji", 1303: "Zoro"}
fmt.Println("m6 = ", m6)
}
2 - map赋值
- map赋值特点
- 赋值过程中,如果新map元素的key与原map元素key 相同 ——> 覆盖(替换)
- 赋值过程中,如果新map元素的key与原map元素key 不同 ——> 添加
func main() {
m7 := make(map[int]string, 1)
m7[100] = "Nami"
m7[20] = "Hello"
m7[3] = "world"
fmt.Println("m7=", m7)
m7[3] = "yellow" // 成功! 将原map中 key 值为 3 的map元素,替换
fmt.Println("m7=", m7)
}
3 - map的使用
- map遍历
for key值, value值 := range map
for key值 := range map
func main() {
// 遍历map
var m8 map[int]string = map[int]string{1: "Luffy", 130: "Sanji", 1301: "Zoro"}
for k, v := range m8 {
fmt.Printf("key:%d --- value:%qn", k, v)
}
// range返回的key/value;省略value打印
for K := range m8 {
fmt.Printf("key:%sn", K)
}
}
- 判断map中key是否存在
- map[下标] 运算:返回两个值
- 第一个表 value 的值,如果value不存在。 nil
- 第二个表 key是否存在的bool类型。存在 true, 不存在false
- map[下标] 运算:返回两个值
func main() {
// 判断 map 中的key 是否存在
var m9 map[int]string = map[int]string{1: "Luffy", 130: "Sanji", 1301: "Zoro"}
if v, has := m9[12]; has { // m9[下标] 返回两个值,第一个是value,第二个是bool 代表key是否存在
fmt.Println("value=", v, "has=", has)
} else {
fmt.Println("false value=", v, "has=", has)
}
}
4 - map的删除与传参
- map删除元素:
delete(map, key)
- 参1:待删除元素的map;参2:key值
- 删除一个不存在的key,不会报错
- map 做函数参数和返回值:传递的是引用
// map做函数参数、返回值,传引用
func mapDelete(m map[int]string, key int) {
delete(m, key) // 删除 m 中 键值为 key的 map 元素
}
func main() {
m := map[int]string{1: "Luffy", 130: "Sanji", 1301: "Zoro"}
fmt.Println("before delete m :", m)
mapDelete(m, 130)
fmt.Println("after delete m :", m)
}
- 案例:封装 wcFunc() 函数。接收一段英文字符串str。返回一个map,记录str中每个“词”出现次数的
package main
import (
"fmt"
"strings"
)
func wordCountFunc(str string) map[string]int {
s := strings.Fields(str) // 将字符串,拆分成字符串切片s
m := make(map[string]int) // 创建一个用于存储 word 出现次数的 map
// 遍历拆分后的字符串切片
for i := 0; i < len(s); i++ {
if _, ok := m[s[i]]; ok { // ok == ture 说明 s[i] 这个key存在
m[s[i]] = m[s[i]] + 1 // m[s[i]]++
} else { // 说明 s[i] 这个key不存在, 第一次出现。添加到map中
m[s[i]] = 1
}
}
return m
}
func wordCountFunc2(str string) (m map[string]int) {
m = make(map[string]int)
arr := strings.Fields(str)
for _, v := range arr {
m[v]++
}
return
}
func main() {
str := "I love my work and I I I I love love love my family too"
//mRet := wordCountFunc(str)
mRet := wordCountFunc2(str)
// 遍历map ,展示每个word 出现的次数:
for k, v := range mRet {
fmt.Printf("%q:%dn", k, v)
}
}
最后
以上就是英勇花生为你收集整理的1-Go强化(1):指针、切片、map一、指针加强二、切片加强三、map加强的全部内容,希望文章能够帮你解决1-Go强化(1):指针、切片、map一、指针加强二、切片加强三、map加强所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复