查看: 58|回复: 0

Go语言初步 5

[复制链接]

45

贡献

36

主题

164

积分

版主

积分
164
QQ
发表于 2026-7-3 20:45:15 | 显示全部楼层 |阅读模式
8.结构体

结构体struct是字段的集合,并且可以通过[struct].[item]的方式来访问和修改。
  1. package main

  2. import "fmt"

  3. type Vertex struct {
  4.         X int
  5.         Y int
  6. }

  7. func main() {
  8.         v := Vertex{1, 2}
  9.         //方式一:通过指针
  10.         p := &v
  11.         p.X = 1e9
  12.         //////////
  13.         //方式二:直接修改
  14.         v.Y = 5
  15.         fmt.Println(v)
  16. }
复制代码
输出结果:{1000000000 5}
这似乎有些类似于python中的字典。

9.数组★
数组的操作比较多,本章内容会比上面长。
数组在 Go 中是值类型,赋值或传参时会复制整个数组。
类型 [n]T 是 n 个类型为 T 的值的数组。
通过关键字var声明。
我们暂时可以理解为python中的列表,下面是两种主要声明方式,一种使用var声明后再赋值,另一种使用`:=`和花括号:
  1. package main

  2. import "fmt"

  3. func main() {
  4.         var a [2]string
  5.         a[0] = "Hello"
  6.         a[1] = "World"
  7.         fmt.Println(a[0], a[1])
  8.         fmt.Println(a)

  9.         primes := [6]int{2, 3, 5, 7, 11, 13}
  10.         fmt.Println(primes)
  11. }
复制代码
输出:
  1. Hello World
  2. [Hello World]
  3. [2 3 5 7 11 13]
复制代码
需要注意的是,上文中a声明时就给定了数组的容量,也就是说它的容量无法再修改了。数组的下标是从0开始的(学过编程的都知道!),查询时下表写在数组名右侧:a[x],但是声明的时候容量写在类型左侧:[x]type,不要弄错了!
数组具有固定大小。另一方面,切片是一种动态大小的、灵活的数组元素视图。在实践中,切片比数组更为常见。
切片是基于数组的,本质上是对数组的一种“切割”(但这不影响原来的数组)。
类型 []T 是元素类型为 T 的切片。就是在数组容量那里留空!当然也可以使用`:=`快速声明赋值。
切片的形成通过指定两个索引,一个低界限和一个高界限,用冒号分隔:
a[low : high],这选择了一个半开范围,包含第一个元素,但不包含最后一个元素。当然同很多语言一样是有默认值的,省略low就是从头开始取(low=0),省略high则取到末尾(high=len(a))。那么对于数组var a [10]int,这些切片表达式是等价的:
  1. a[0:10]
  2. a[:10]
  3. a[0:]
  4. a[:]
复制代码

以下表达式创建了一个包含 primes 的第 1 至第 3 个元素的切片:
  1. primes := [6]int{2, 3, 5, 7, 11, 13}
  2. var s []int = primes[1:4]        //or s:=primes[1:4]
复制代码
这样s就是[3 5 7](即`[3]int{3, 5, 7}`)了。
我们说切片不影响原来的数组,是因为它本质上是数组的引用。
你可以理解为一种指针:

这张图片很好体现了b对a的指向关系(伪代码示意)。如果a的内容被修改了,那么b也会受到影响。如果你在修改b,那么因为b是指向a的,所以原则上你是在修改a,比如说你使用b[1]=114514,如果这时又有c指向a的[1:3],那么c会由原来的[2 3]变为[114514 3]。
我们来看示例(JetBrainsAcademy):
  1. package main

  2. import "fmt"

  3. func main() {
  4.         names := [4]string{
  5.                 "John",
  6.                 "Paul",
  7.                 "George",
  8.                 "Ringo",
  9.         }
  10.         fmt.Println(names)//[1]

  11.         a := names[0:2]
  12.         b := names[1:3]
  13.         fmt.Println(a, b)//[2]

  14.         b[0] = "XXX"
  15.         fmt.Println(a, b)//[3]
  16.         fmt.Println(names)//[4]
  17. }
复制代码
输出:
  1. [John Paul George Ringo]
  2. [John Paul] [Paul George]
  3. [John XXX] [XXX George]
  4. [John XXX George Ringo]
复制代码
接下来是难点,我们必须复习一下:
  • 数组字面量:[3]bool{true, true, false}中,方括号里写了 3,表示这是一个长度为 3 的数组。
  • 数组在 Go 中是值类型,赋值或传参时会复制整个数组。
接下来看切片字面量:
我们说[]type创建的是对type类型数组的切片,尽管它的“指针”还没有指向。
但是我们会看到:[]bool{true, true, false},这是切的什么呢?
切片本身不存储数据,它只是一个“视图”,指向底层的一个数组。
从写法上看,切片字面量和数组字面量几乎一样,唯一的区别就是方括号里有没有写长度。
但从执行过程上看,当你写 []bool{true, true, false} 时,Go 编译器会自动帮你创建一个长度为 3 的匿名数组,里面放着这三个布尔值,然后让这个切片指向它。这个过程等价于:
  1. // 先创建一个数组
  2. arr := [3]bool{true, true, false}
  3. // 再基于该数组生成一个切片(引用整个数组)
  4. slice := arr[:]
复制代码
所以,切片字面量就是一种“省略了长度”的数组字面量,但底层依然存在一个数组,只是这个数组你看不见名字,由切片间接管理。
为什么有这种设计?因为在实际开发中,我们更常用切片(动态、灵活),而很少直接操作数组。切片字面量让我们可以像写数组一样方便地初始化数据,但又不必操心长度——长度由编译器根据元素个数自动推断。
同样的,你也可以创建一个结构体数组的切片:
  1. s := []struct {
  2.                 i int
  3.                 b bool
  4.         }{
  5.                 {2, true},
  6.                 {3, false},
  7.                 {5, true},
  8.                 {7, true},
  9.                 {11, false},
  10.                 {13, true},
  11.         }
复制代码
(是不是有点像js)
s就是[{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]
切片既有 长度 也有 容量。
切片的长度是指它所包含的元素数量。
切片的容量是指其底层数组中的元素数量,从切片的第一个元素开始计数,更像是一种能力(capability)。
可以使用表达式 len(s) 和 cap(s) 获取切片 s 的长度和容量。
如果容量足够,可以通过重新切片来扩展切片的长度。
  1. package main

  2. import "fmt"

  3. func main() {
  4.         s := []int{2, 3, 5, 7, 11, 13}
  5.         printSlice(s)

  6.         // Slice the slice to give it zero length.
  7.         s = s[:0]
  8.         printSlice(s)

  9.         // Extend its length.
  10.         s = s[:4]
  11.         printSlice(s)

  12.         // Drop its first two values.
  13.         s = s[2:]
  14.         printSlice(s)
  15. }

  16. func printSlice(s []int) {
  17.         fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
  18. }
复制代码

输出:
  1. len=6 cap=6 [2 3 5 7 11 13]
  2. len=0 cap=6 []
  3. len=4 cap=6 [2 3 5 7]
  4. len=2 cap=4 [5 7]
复制代码
注意之前所说的,数组在 Go 中是值类型,赋值或传参时会复制整个数组,所以不要疑惑为什么s已经被切过了还能“复原”。
对于一个刚刚声明未赋值的切片:var s []int
它是零值切片,值是nil。
一个nil切片的长度和容量都是0,并且没有底层数组。
你可以写:s==nil,这是真的。
你可以使用内置的 make 函数可以创建切片;这就是你创建动态大小数组的方法。
make 函数会分配一个清零的数组,并返回一个指向该数组的切片:
  1. a := make([]int, 5)  // len(a)=5
复制代码

要指定容量,请传递一个第三参数给 make:
  1. b := make([]int, 0, 5) // len(b)=0, cap(b)=5
  2. b = b[:cap(b)] // len(b)=5, cap(b)=5
  3. b = b[1:]      // len(b)=4, cap(b)=4
复制代码
例子(JetbrainsAcademy):
  1. package main

  2. import "fmt"

  3. func main() {
  4.         a := make([]int, 5)
  5.         printSlice("a", a)

  6.         b := make([]int, 0, 5)
  7.         printSlice("b", b)

  8.         c := b[:2]
  9.         printSlice("c", c)

  10.         d := c[2:5]
  11.         printSlice("d", d)
  12. }

  13. func printSlice(s string, x []int) {
  14.         fmt.Printf("%s len=%d cap=%d %v\n",
  15.                 s, len(x), cap(x), x)
  16. }
复制代码
输出:
  1. a len=5 cap=5 [0 0 0 0 0]
  2. b len=0 cap=5 []
  3. c len=2 cap=5 [0 0]
  4. d len=3 cap=3 [0 0 0]
复制代码
切片可以包含任何类型,包括其他切片。这有点像多重指向,很多条道路收束在同一个点(内存)上(这个比喻不完全正确!)。
例子(JetbrainsAcademy):
  1. package main

  2. import (
  3.         "fmt"
  4.         "strings"
  5. )

  6. func main() {
  7.         // Create a tic-tac-toe board.
  8.         board := [][]string{
  9.                 []string{"_", "_", "_"},
  10.                 []string{"_", "_", "_"},
  11.                 []string{"_", "_", "_"},
  12.         }

  13.         // The players take turns.
  14.         board[0][0] = "X"
  15.         board[2][2] = "O"
  16.         board[1][2] = "X"
  17.         board[1][0] = "O"
  18.         board[0][2] = "X"

  19.         for i := 0; i < len(board); i++ {
  20.                 fmt.Printf("%s\n", strings.Join(board[i], " "))
  21.         }
  22. }
复制代码
输出:
  1. X _ X
  2. O _ X
  3. _ _ O
复制代码
由于篇幅过长,向切片中添加元素下一节继续讲述。
Hi~ o(* ̄▽ ̄*)ブ I Love U ~
查看帮助

相关侵权、举报、投诉及建议等,请按帮助内指引进行反馈

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部