当前位置: 首页 > news >正文

【Go】C++ 转 Go 第(三)天:defer、slice(动态数组) 与 map

本专栏文章持续更新,新增内容使用蓝色表示。

食用指南

本文适合 C++ 基础的朋友,想要快速上手 Go 语言。可直接复制代码在 IDE 中查看,代码中包含详细注释和注意事项。

Go 的环境搭建请参考以下文章:

【Go】C++ 转 Go 第(一)天:环境搭建 Windows + VSCode 远程连接 Linux -CSDN博客

defer 关键字

defer 是 Go 语言中用于延迟执行的关键字,类似于 C++ 中的析构函数和 Java 中的 finally 块,主要用于资源清理和异常处理。

  • 执行顺序:LIFO(后进先出)

  • 参数求值:defer语句执行时立即求值

  • 执行时机:在return语句之后、函数真正返回之前执行

  • 返回值影响:可以修改命名返回值

defer.go 

package mainimport ("fmt"
)// 1. 调用顺序-先进后出
func func1() {fmt.Println("------func1------")
}func func2() {fmt.Println("------func2------")
}func func3() {fmt.Println("------func3------")
}// 2. defer&return 调用顺序
func deferFunc() {fmt.Println("-------defer called-------")
}
func returnFunc() int {fmt.Println("-------return called-------")return 6
}
func returnAndDefer() int {defer deferFunc() // defer是当前函数声明周期全部结束之后才会调用fmt.Println("-------return & Defer-------")return returnFunc()
}func main() {// defer关键字,类似于C++中的析构函数,类似java中的final// 支持多个,按照顺序依次入栈,先进后出// 比如:最后关闭文件// 1. 调用顺序defer func1()defer func2()defer func3()fmt.Println("main::hello go 1")// 2. return & deferreturnAndDefer()
}

执行结果

slice——Go语言的动态数组

数组与Slice的基础对比

arrayAndSlice.go 

package mainimport ("fmt"
)// 值拷贝
func printArray(nums [2]int) {fmt.Println("------printArray------")nums[0] = 99for _, value := range nums {fmt.Println(value)}
}// 依旧是值传递,只不过传递的是指针
func printSlice(slice []string) {fmt.Println("------printSlice------")slice[0] = "hi"slice[1] = "Jade"for _, value := range slice {fmt.Println(value)}
}func main() {// 一、固定长度的数组var myArray1 [3]intmyArray2 := [5]int{10, 11, 12}myArray3 := [2]int{20, 21}// 1. 遍历方式// 1)硬编码长度(不推荐)// for i := 0; i < 5; i++// 2)使用 lenfmt.Println("--------len---------")fmt.Printf("myArray1 type : %T \n", myArray1)for i := 0; i < len(myArray1); i++ { // 这个 i 只在循环内有效myArray1[i] = ifmt.Println(myArray1[i])}// 3)使用 range,返回的是索引和值// for index,value:=range myArray2	不需要的使用匿名(_)fmt.Println("--------range---------")fmt.Printf("myArray2 type : %T \n", myArray2)for _, value := range myArray2 {fmt.Println(value)}// 2. 函数传参,严格匹配类型fmt.Println("------BeforePrintArray3------")for _, value := range myArray3 {fmt.Println(value)}printArray([2]int(myArray3))fmt.Println("------AfterPrintArray3------")for _, value := range myArray3 {fmt.Println(value)}// 二、动态数组 切片 slice// 实际上是指向一块内存地址的指针mySlice1 := []string{"hello", "world"}fmt.Println("--------BeforePrintSlice---------")fmt.Printf("mySlice1 type : %T \n", mySlice1)for _, value := range mySlice1 {fmt.Println(value)}printSlice(mySlice1)fmt.Println("--------AfterPrintSlice---------")fmt.Printf("mySlice1 type : %T \n", mySlice1)for _, value := range mySlice1 {fmt.Println(value)}
}

执行结果

Slice的声明与内存管理

sliceDeclaration.go 

package mainimport ("fmt""unsafe"
)func main() {// slice 声明方式// 1. 有初始值mySlice1 := []int{1, 2, 3}fmt.Printf("mySlice1 Type is %T , len=%d, value=%v \n", mySlice1, len(mySlice1), mySlice1)// 2. 无初始值// 1)nil切片,默认没有分配空间,底层数组指针为 nilvar mySlice2 []intfmt.Printf("mySlice2 Type is %T , len=%d, value=%v \n", mySlice2, len(mySlice2), mySlice2)// mySlice2[0] = 1 // 此时赋值会越界// 需要为其开辟空间mySlice2 = make([]int, 5)mySlice2[0] = 99fmt.Printf("mySlice2 Type is %T , len=%d, value=%v \n", mySlice2, len(mySlice2), mySlice2)// 2)无初始值,分配空间(两种方法均可)var mySlice3 []int = make([]int, 2)mySlice4 := make([]int, 4) // 较为常见fmt.Printf("mySlice3 Type is %T , len=%d, value=%v \n", mySlice3, len(mySlice3), mySlice3)fmt.Printf("mySlice4 Type is %T , len=%d, value=%v \n", mySlice4, len(mySlice4), mySlice4)// 判断slice是否为空(没有空间)var mySlice5 []int // nil切片if mySlice5 == nil {fmt.Println("mySlice5 is nil,没有空间")} else {fmt.Println("mySlice5 is nil,有空间")}mySlice6 := make([]int, 0) // 空切片,底层数组指针指向一个空数组(但不是nil)if mySlice6 == nil {fmt.Println("mySlice6 is nil,没有空间")} else { // 语法规定,else必须和}{在同一行fmt.Println("mySlice6 is nil,有空间")}// 以下仅为补充,暂时不用看懂// 查看底层指针fmt.Printf("mySlice5 指针: %p\n", (*[0]int)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&mySlice5)))))fmt.Printf("mySlice6 指针: %p\n", (*[0]int)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&mySlice6)))))// nil 切片和空切片的行为几乎相同,但在序列化、反射和一些库函数的处理上会有差异
}

执行结果

Slice操作与内存共享机制

sliceUsage.go 

package mainimport "fmt"func main() {// 1. 切片容量增加// 第一个参数是类型,第二个是长度,第三个是容量// 和C++的 reserve() 函数有点类似var nums = make([]int, 2, 3)fmt.Printf("nums type is %T, len=%d,cap=%d,nums=%v \n", nums, len(nums), cap(nums), nums)// 追加元素// 容量不够,会开辟2倍cap的空间nums = append(nums, 2, 3)fmt.Printf("nums type is %T, len=%d,cap=%d,nums=%v \n", nums, len(nums), cap(nums), nums)// 2. 切片截取s := []int{1, 2, 3, 4, 5}// 左闭右开区间s1 := s[0:2]s2 := s[2:4]fmt.Printf("s1 type is %T, len=%d,cap=%d,nums=%v \n", s1, len(s1), cap(s1), s1)fmt.Printf("s2 type is %T, len=%d,cap=%d,nums=%v \n", s2, len(s2), cap(s2), s2)// 从结果可以发现尽管 s1 和 s2 的len一样,但是容量不一样// 从索引0开始 → 容量=5-0=5// 从索引2开始 → 容量=5-2=3// 共享底层数组// 相当于是浅拷贝,只拷贝了地址fmt.Println("\n=== 浅拷贝修改验证 ===")s1[0] = 100s2[0] = 300fmt.Printf("修改后 s:  %v\n", s)fmt.Printf("修改后 s1: %v\n", s1)fmt.Printf("修改后 s2: %v\n", s2)// 深拷贝fmt.Println("\n=== 深拷贝修改验证 ===")s3 := make([]int, 3)copy(s3, s)// copy(s3, s[3:4])	// 也可以指定范围s3[0] = 99fmt.Printf("修改后 s:  %v\n", s)fmt.Printf("修改后 s3:  %v\n", s3)
}

执行结果

map——Go语言的哈希表

map.go

package mainimport ("fmt"
)// 值传递,浅拷贝,传递的是指针
func PrintMap(myMap map[int]string) {// 此处如果修改 myMap 会影响到原 mapfor key, value := range myMap {fmt.Printf(" key=%d, value=%s \n", key, value)}
}// 深拷贝
func CopyMap(myApp1, myApp2 map[int]string) {for key, value := range myApp1 {myApp2[key] = value}
}
func main() {// map 声明,map[key]valuefmt.Println("=======声明=======")// 1. 方式一var myMap1 map[string]stringif myMap1 == nil {fmt.Println("myMap1 是一个空map")} else {fmt.Println("myMap1 是一个非空map")}// 分配空间myMap1 = make(map[string]string, 2)myMap1["c"] = "C++"myMap1["p"] = "python"myMap1["g"] = "Go"fmt.Println(myMap1)// 2. 方式二myMap2 := make(map[int]string)myMap2[1] = "Go"myMap2[2] = "C++"fmt.Println(myMap2)// 3. 方式三myMap3 := map[int]string{1: "C++",2: "Go",3: "Python", // 注意此处也要有逗号}fmt.Println(myMap3)// 遍历// go为了避免开发者依赖顺序,所以是乱序的,每次的结果可能不同fmt.Println("\n=======遍历=======")PrintMap(myMap3)// 修改fmt.Println("\n=======修改=======")myMap3[3] = "Java"PrintMap(myMap3)// 删除fmt.Println("\n=======删除=======")delete(myMap3, 3)PrintMap(myMap3)// 深拷贝fmt.Println("\n=======深拷贝=======")myMap4 := make(map[int]string)CopyMap(myMap3, myMap4)PrintMap(myMap4)}

执行结果


如有问题或建议,欢迎在评论区中留言~

http://www.dtcms.com/a/502993.html

相关文章:

  • 【大模型微调】LLaMA Factory 微调 LLMs VLMs
  • 服务器管理:构建与维护高效服务器环境的指南
  • wordpress 网站生成app中山免费建站
  • 使用搭载Ubuntu的树莓派开启热点
  • 存算一体架构的先行者:RustFS在异构计算环境下的探索与实践
  • asp access网站建设源代码网站的开发流程可以分为哪三个阶段
  • SAUP论文提到的S2S Backbone Models是什么
  • 实战量化Facebook OPT模型
  • C 标准库函数 | strcmp, strlen
  • 图像处理~多尺度边缘检测算法
  • 网站集约化建设必要性wordpress 媒体库外链
  • springboot整合redis-RedisTemplate集群模式
  • Spring AOP 实战案例+避坑指南
  • 第三章 栈和队列——课后习题解练【数据结构(c语言版 第2版)】
  • Kubernetes Ingress与安全机制
  • 【企业架构】TOGAF架构标准规范-机会与解决方案
  • apache建设本地网站wordpress修改成中文字体
  • windows平台,用pgloader转换mysql到postgresql
  • Linux驱动第一期1-10-驱动基础总结
  • 我的WordPress网站梅林固件做网站
  • 分库分表:基础介绍
  • 使用css `focus-visible` 改善用户体验
  • AI人工智能-深度学习的基本原理-第二周(小白)
  • 【2070】数字对调
  • 【AI智能体】Coze 提取对标账号短视频生成视频文案实战详解
  • IOT项目——ESP系列
  • 【成长纪实】Dart 与 ArkTS 函数与类的对比学习:从 Flutter 到 HarmonyOS
  • 基于 JETSON+FPGA+GMSL+AI 车载视频采集与存储系统设计(二)系统测试
  • Flutter Event Loop
  • LeetCode 1287.有序数组中出现次数超过25%的元素