不采用数组的时候,处理问题,需要定义很多变量

  1. func main() {
  2. // 求3个数的平均数
  3. n1 := 10.289
  4. n2 := 9.98
  5. n3 := 10.645
  6. var total = n1 + n2 + n3
  7. var avgNum = total / 3
  8. _avgNum := fmt.Sprintf("%.2f", avgNum) // 保留两位小数
  9. fmt.Println("avgNum = ", avgNum) // avgNum = 10.304666666666666
  10. fmt.Println("_avgNum = ", _avgNum) // avgNum = 10.304666666666666
  11. }

以上方法不利于数据的管理,不够灵活

引入数组

介绍

数组可以存放多个同一类型数据
在Go中,数组类型是值类型

使用数组修改上面的代码:

  1. var list [3]float64
  2. list[0] = 10.289
  3. list[1] = 9.98
  4. list[2] = 10.645
  5. fmt.Printf("list 类型: %T, 值: %v \n", list, list)
  6. var t float64
  7. for i := 0; i < len(list); i++ {
  8. t += list[i]
  9. }
  10. fmt.Println("t = ", t/3)
  11. var avg = t / float64(len(list))
  12. var _avg = fmt.Sprintf("%.2f", avg)
  13. fmt.Println("_avg = ", _avg)
  14. // list 类型: [3]float64, 值: [10.289 9.98 10.645]
  15. // t = 10.304666666666666
  16. // _avg = 10.30

数组内存探究

指针
%p 表示为十六进制,并加上前导的0x

  1. var arrInt [3]int // int占8个字节
  2. fmt.Printf("arrInt 类型: %T, 值: %v \n", arrInt, arrInt)
  3. fmt.Printf("arrInt 的内存地址 = %p \n", &arrInt)
  4. for i := 0; i < len(arrInt); i++ {
  5. fmt.Printf("list[%v]的地址是:%p, 值:%v \n", i, &arrInt[i])
  6. }
  7. arrInt 类型: [3]int, 值: [0 0 0] // 初始值是默认零值
  8. arrInt 的内存地址 = 0xc000012180
  9. list[0]的地址是:0xc000012180, 值:0
  10. list[1]的地址是:0xc000012188, 值:0 // &list[0] + 8个字节
  11. list[2]的地址是:0xc000012190, 值:0 // &list[1] + 8个字节

说明

计算机分配内存地址,就是首地址(数组元素首地址)
数组里面的元素的地址,是在内存中连续分配的,方便快速的读取数组中的元素,因此,只需要记录数组的首地址就可以了。

内存间隔

第二个元素的地址 = 第一个元素的地址 + 这个数组类型占用的字节数
int 类型占8个字节
int32 占4个字节

  1. var arrInt [3]int32
  2. fmt.Printf("arrInt 类型: %T, 值: %v \n", arrInt, arrInt)
  3. fmt.Printf("arrInt 的内存地址 = %p \n", &arrInt)
  4. for i := 0; i < len(arrInt); i++ {
  5. fmt.Printf("list[%v]的地址是:%p, 值:%v \n", i, &arrInt[i])
  6. }
  7. arrInt 类型: [3]int32, 值: [0 0 0]
  8. arrInt 的内存地址 = 0xc0000140a0
  9. list[0]的地址是:0xc0000140a0, 值:0
  10. list[1]的地址是:0xc0000140a4, 值:0 // &list[0] + 4个字节
  11. list[2]的地址是:0xc0000140a8, 值:0 // &list[1] + 4个字节

内存图解

image.png

4种数组初始化的方式

  1. // 初始化数组
  2. var arr1 [3]int = [3]int{1, 2, 3}
  3. fmt.Println("arr1 = ", arr1) // arr1 = [1 2 3]
  4. var arr2 = [3]int{10, 20, 30}
  5. fmt.Println("arr2 = ", arr2) // arr2 = [10 20 30]
  6. var arr3 = [...]int{0, 1, 2}
  7. fmt.Println("arr3 = ", arr3) // arr3 = [0 1 2]
  8. var arr4 = [...]int{1: 100, 3: 500, 0: 11}
  9. fmt.Println("arr4 = ", arr4) // arr4 = [11 100 0 500]

遍历数组的方式

1、常规for循环
2、for range
返回下标和元素值,类似于JavaScript种for in遍历对象,拿到的是属性和属性值

  1. 语法
  2. for index, value := range array {
  3. }

使用,字符串数组

  1. var arr02 = [3]string{"luffy01", "luffy02", "luffy03"}
  2. for index, value := range arr02 {
  3. fmt.Println("index = ", index, "value = ", value)
  4. }
  5. index = 0 value = luffy01
  6. index = 1 value = luffy02
  7. index = 2 value = luffy03

注意事项

  1. 数组一旦声明/定义,长度和类型是固定的,不能修改。长度是数据类型的一部分
    1. 如果需要存储更多的元素,就需要先创建一个更长的数组,再把原来数组里的值复制到新数组里
  2. 数组元素既可以是值类型又可以是引用类型,但是不能混用(类型)
  3. 数组定义之后,如果没有赋值,默认值就是对应类型的零值
    1. string: “”,bool: false, int: 0
  4. 定义/初始化数组的时候,应该指定数组的范围
  5. 数组属于值类型,在函数之间传递数组属于值拷贝,不会应该原数组
    1. 如若数组的长度超级大,100万个元素,传递给函数的时候,相当于复制了8M的内存空间,这种情况建议使用指针传递,只需要复制8个字节就可以,指针类型传递可以改变原数组,但是使用的时候要注意数组的数据同步问题。
  1. func test(arr *[3]int) {
  2. (*arr)[1] = 10
  3. }
  4. // main函数
  5. // 指针数组传递
  6. var list1 [3]int
  7. fmt.Println("list1 = ", list1) // list1 = [0 0 0]
  8. test(&list1)
  9. fmt.Println("list1 = ", list1) // list1 = [0 10 0]

练习

1、byte类型的数组,长度为26,存放英文大写字母 A-Z,并输出

  1. package main
  2. import "fmt"
  3. func main() {
  4. // byte类型的数组,长度为26,存放英文大写字母 A-Z
  5. // 字符可以参与运算: 'A' + 1 = 'B' // ascii
  6. var myChars [26]byte
  7. for i := 0; i < len(myChars); i++ {
  8. myChars[i] = 'A' + byte(i)
  9. fmt.Printf("%c ", myChars[i])
  10. }
  11. fmt.Println("myChars = ", myChars)
  12. }
  13. A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
  14. myChars = [65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90]

2、求数组最大值, 并输出对应下标

  1. // 求最大值, 并输出对应下标
  2. var list = [5]int{1, 0, -5, 6, 2}
  3. var maxV int = list[0]
  4. var maxIndex int = 0
  5. for i := 0; i < len(list); i++ {
  6. if maxV < list[i] {
  7. maxV = list[i]
  8. maxIndex = i
  9. }
  10. }
  11. fmt.Println("list = ", list)
  12. fmt.Println("maxV = ", maxV, "maxIndex = ", maxIndex)

3、求平均值

  1. // 求平均值
  2. var list01 [5]int = [...]int{1, -1, 20, 40, 61}
  3. sum := 0
  4. for _, v := range list01 {
  5. sum += v
  6. }
  7. fmt.Printf("sum = %v, list01平均值:%v \n", sum, sum/len(list01))
  8. // 平均值保留小数
  9. fmt.Printf("sum = %v, list01平均值:%v \n", sum, float64(sum)/float64(len(list01)))
  10. sum = 121, list01平均值:24
  11. sum = 121, list01平均值:24.2

4、随机生成5个数,并反转打印

import “math/rand”
rand包实现了伪随机数生成器
随机数从资源生成。包水平的函数都使用的默认的公共资源。该资源会在程序每次运行时都产生确定的序列。如果需要每次运行产生不同的序列,应使用Seed函数进行初始化。默认资源可以安全的用于多go程并发。
func (r *Rand) Intn(n int) int
返回一个取值范围在[0,n)的伪随机int值,如果n<=0会panic。

  1. // 随机生成5个数,并反转打印
  2. var intList [5]int
  3. // 为了每次生成的随机数不一样,需要设置种子 Seed, 通过时间戳的方式生成种子,以保证每次都不一样
  4. rand.Seed(time.Now().UnixNano())
  5. for i := 0; i < len(intList); i++ {
  6. intList[i] = rand.Intn(100)
  7. }
  8. fmt.Println("intList = ", intList) // intList = [81 87 47 59 81]
  9. // 反转
  10. // for i := len(intList) - 1; i >= 0; i-- {
  11. // fmt.Printf("intList[%v] = %v \n", i, intList[i])
  12. // }
  13. fmt.Println("交换前:", intList)
  14. var tmp int
  15. for i := 0; i < len(intList)/2; i++ {
  16. tmp = intList[len(intList)-1-i]
  17. intList[len(intList)-1-i] = intList[i]
  18. intList[i] = tmp
  19. }
  20. fmt.Println("交换后:", intList)
  21. intList = [61 98 37 41 11]
  22. 交换前: [61 98 37 41 11]
  23. 交换后: [11 41 37 98 61]
  24. // 打印值
  25. PS D:\go-project\src\go_code\array01\main> go run .\main.go
  26. intList = [81 87 47 59 81]
  27. PS D:\go-project\src\go_code\array01\main> go run .\main.go
  28. intList = [81 87 47 59 81]
  29. PS D:\go-project\src\go_code\array01\main> go run .\main.go
  30. intList = [82 30 76 56 10]
  31. PS D:\go-project\src\go_code\array01\main> go run .\main.go
  32. intList = [24 95 48 15 19]
  33. PS D:\go-project\src\go_code\array01\main>