Go--day1| 青训营笔记

63 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第1天

这几天在学Go语言,这是第一篇笔记。

安装delve方便后续调试

首先我安装了delve,它是一个调试工具,类似于gdb:
delve项目地址

其实也可以用集成开发环境进行调试,例如goland,我这里使用delve是因为我觉得使用命令行工具来调试更能锻炼自己,因为某些情况下只能使用命令行工具调试,现在使用会对以后有所帮助。

数组和切片

Go的切片类型提供了一种方便高效的处理类型数据序列的方法。切片类似于其他语言中的数组,但具有一些不寻常的属性。

切片类型是建立在Go数组类型之上的抽象,因此要理解切片,我们必须首先理解数组。

数组类型定义指定长度和元素类型。例如,类型[4]int表示由四个整数组成的数组。数组的大小是固定的;其长度是其类型的一部分([4]int[5]int是不同的、不兼容的类型)。数组可以通常进行索引,因此表达式s[n]从零开始访问第n个元素。

Go的数组是值。数组变量表示整个数组;它不是指向第一个数组元素的指针(如C中的情况), 因为我C语言用的多,我便好奇该如何获得一个数组的地址并打印出来,查询得知可以这样:

package main
import "fmt"
import "unsafe"

func main() {
	array := [5]int{}
	fmt.Println(unsafe.Pointer(&array))
}

这样就得到了数组的地址,同样,也可以通过强制类型转换得到一个地址所在的数组。

数组的本质是什么呢?

通过delve,我研究了数组的本质,发现其和C语言一模一样————一段连续地址空间,元素依次排列:

(dlv) n
> main.main() ./2.go:10 (PC: 0x100adb880)
     5:
     6: var p = fmt.Println
     7:
     8: func main() {
     9:         array := [5]int{1,2,3,4,5}
=>  10:         p(array)
    11: }
(dlv) p array
[5]int [1,2,3,4,5]
(dlv) p &array
(*[5]int)(0x1400010bef0)
(dlv) p *(*int)(0x1400010bef0)
1
(dlv) p *(*int)(0x1400010bef8)
2
(dlv) p *(*int)(0x1400010bf00)
3
(dlv) p *(*int)(0x1400010bf08)
4
(dlv) p *(*int)(0x1400010bf10)
5
(dlv) 

我们再来看看切片的本质,同样也是通过delve调试,我发现切片无异于一个结构体,结构体变量是cap、len、指向数组的指针:

(dlv) n
> main.main() ./2.go:10 (PC: 0x102ae78c8)
     5:
     6: var p = fmt.Println
     7:
     8: func main() {
     9:         slice := []int{1,2,3,4,5}
=>  10:         p(slice)
    11: }
(dlv) p slice
[]int len: 5, cap: 5, [1,2,3,4,5] // 切片内容
(dlv) p &slice
(*[]int)(0x1400010bf38) // 切片地址
(dlv) p *(*[3]int)(0x1400010bf38) // 这里看看切片地址下的三个int到死是什么
[3]int [1374390763520,5,5] // 发现有两个5,推测第一个元素是数组的地址
(dlv) p *(*[5]int)(1374390763520) // 于是打印此地址下的五个元素
[5]int [1,2,3,4,5] // 证实猜想
(dlv)