基本语法
声明
基本语法:
声明:**<font style="color:rgb(51, 51, 51);">var</font>** a [len]**<font style="color:rgb(51, 51, 51);">type</font>**
初始化
初始化:左边——全局<font style="color:rgb(51, 51, 51);">var a =</font>
/ 局部<font style="color:rgb(51, 51, 51);">a :=</font>
+ 右边——数值<font style="color:rgb(51, 51, 51);">[len]type{...,...}</font>
实例
全局:
var arr0 = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}
局部:
a := [3]int{1, 2} // 未初始化元素值为 0。
b := [...]int{1, 2, 3, 4} // 通过初始化值确定数组长度。
//注意省略也是要。。。
c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。
//类型为结构,这是一维度数组
d := [...]struct {
name string
age uint8
}{
{"user1", 10}, // 可省略元素类型。
{"user2", 20}, // 别忘了最后一行的逗号!!!
}
总结
- 全局
var a
/ 局部a:=
- 由于数组是值类型,声明与初始化为零值作用相同
- 声明
**<font style="color:rgb(51, 51, 51);">var</font>** a [len]**<font style="color:rgb(51, 51, 51);">type</font>**
- 初始化为零值:
**<font style="color:rgb(51, 51, 51);">var a = [5]type{}</font>**
/a := [len]type{..., ...}
如果是多维数组,则 类型改成[len_1][len_2]type{{...}, {...}}
- 声明
- 初始化
- {}怎么塞?
- 一维数组
- 不完全赋值
[5]int{1, 2, 3}
/[5]string{3: "hello world", 4: "tom"}
- 完全赋值
[5]int{1, 2, 3, 4, 5}
/[...]int{1, 2, 3, 4, 5, 6}
- 不完全赋值
- 多维数组
[2][3]int{{1, 2, 3}, {7, 8, 9}}
- 可省略第一个
[...][3]int{{1, 2, 3}, {7, 8, 9}}
- 一维数组
- 用下标,挨个赋值
arr [i] =
- {}怎么塞?
- 组合
**类型——注意一下struct类型
**
性质
数组:是同一种数据类型的固定长度的序列。
- 不可变性
数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。 - 访问
- 数组可以通过下标进行访问,下标是从0开始,
arr[i]
,最后一个元素下标是:len-1 - 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
- 数组可以通过下标进行访问,下标是从0开始,
- 类型
- 数组是值类型
- 赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
- 支持 “==”、“!=” 操作符,一方面因为是值,可比较,另一方面因为内存总是被初始化过的。
- 长度
长度是数组类型的一部分,因此,**<font style="color:rgb(51, 51, 51);">var</font>** a[<font style="color:rgb(0, 128, 128);">5</font>] **<font style="color:rgb(51, 51, 51);">int</font>**
和**<font style="color:rgb(51, 51, 51);">var</font>** a[<font style="color:rgb(0, 128, 128);">10</font>]**<font style="color:rgb(51, 51, 51);">int</font>**
是不同的类型。 - 类型选择
类型不仅可以是int/foalt,还可以是struct类型 - 类型格式:
指针数组[n]*T
,数组指针*[n]T
。
- 数组是值类型
- 内置函数 len 和 cap 都返回数组长度 (元素数量)。
基本应用
数组的截取
:::info
:::
注意
a[2:len(a)] 等价于 a[2:]
- 不支持负数索引 如
a[-1:]
- 数组截取之后一定是切片
多维数组
:::info 声明/初始化
:::
- 编写原则和一维数组相同,就是注意数值是
[len_1][len_2]type{{...}, {...}}
全局
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
var arr1 = [...][3]int{{1, 2, 3}, {7, 8, 9}}
局部:
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。
//方便不数数
//可直接打印
fmt.Println(a, b)
- 更直观的写法
注意:以上代码中倒数第二行的 } 必须要有逗号,因为最后一行的 } 不能单独一行,也可以写成这样:
:::info 创建一个各维度数不一样的多维数组
:::
没有初始化的数组元素值为0(数值0会显示,字符零值为""
不显示)
结果
空数组
- 声明一个空数组,后面可以给其赋值
- 不可用append函数
函数参数
:::info 数组做参数
:::
数组由于是值类型,给函数传递参数,本质是值传递,即实参的值复制给形参,副本怎么修改不改变原本数组的值
:::info 数组指针做参数
:::
那上面情况怎么改变呢?——数组指针
package main
import "fmt"
func main(){
a := [5]int{1,2,3,4,5}
modify(&a)
fmt.Println(a)
}
func modify(p *[5]int){
(*p)[2] = 100//*p就等价与数组a了
}
这里注意
p = &a
那么*p
就等同于数组a,(*p)[3]
就是数组的第三号元素
(之前有个特别不好的,就是*p = &a[2]
,由于go指针不支持和整数的算术运算,所以只能指向该元素,不可改变)
与c语言的差别
c语言 | go语言 | |
---|---|---|
语法 | type name [维数] 如int a[10] |
**<font style="color:rgb(51, 51, 51);">var</font>** a [len]**<font style="color:rgb(51, 51, 51);">int</font>** ,比如:**<font style="color:rgb(51, 51, 51);">var</font>** a [<font style="color:rgb(0, 128, 128);">5</font>]**<font style="color:rgb(51, 51, 51);">int</font>** |
类型 | arr会自动转换成地址/指针 | go的数组是值类型,可以进行数值比较 要得到指针只有&arr[i] |
遍历/访问
for…range
关于for …range 是 go 自身的语法,可以用来遍历数据结构,有如下数据结构可以遍历
- 切片 slice
- 数组 array
- map 集合
- channel 通道
我们分别来看看可以如何使用他们,for…range 相当于一个迭代器,可以遍历数据结构的键/索引 和值
:::info 数组array
:::
for index, v := range arr_1 {
}
获得的 index和v分别是数组arr_1的索引和值,index从 0 到 len(arr_1) - 1
一维数组遍历
for i := 0; i < len(a); i++ {
}
for index, v := range a {
}
多维数组的遍历
传统方式:
常用方式——for..range:
package main
import (
"fmt"
)
func main() {
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
for k2, v2 := range v1 {
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
}
fmt.Println()
}
}
注意:Printf 与 Println区别
修改
通过指针修改
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3}
p := &s[2] // *int, 获取底层数组元素指针。
*p += 100
fmt.Println(s)
}
通过下标
arr[i] = 10
数组的拷贝和传参
涉及到指针
传参
- 值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。
如果用切片会怎么样呢? - 若想通过自定义修改数组,那就需要指针
拷贝
拷贝问题见切片,会有具体的对比
练习
随机数
:::info 如何产生随机数?——三步
:::
- 引入包
import "math/rand"
- 设置种子
rand.Seed(参数)
,那么设置什么参数呢?- 参数性质:
如果种子的参数不变,那么每次程序go run/go build一次, 产生的随机数, 那都是一样的 - 所以我们常常把当前时间当作种子参数,方法如下
- 引入包
import "time"
rand.Seed(time.Now().UnixNano)
- 引入包
- 参数性质:
- 产生随机数
- 产生随机的数,通常数值很大,
rand.Int()
- 在一定范围内产生随机数 ,比如在100以内
rand.Intn(100)
- 产生随机的数,通常数值很大,
package main
import "fmt"
import "math/rand"
import "time"
func main() {
rand.Seed(time.Now().UnixNano)//时间作为参数
for i :=100; i<5; i++{
fmt.Println("rand = ", rand.Intn(100))//随机数范围在100以内
}
}
为什么上例我不显示结果,但下面的例子我显示了结果
func main(){
// 若想做一个真正的随机数,要种子
// seed()种子默认是1
//rand.Seed(1)
rand.Seed(time.Now().Unix())
var b[10] int
for i:=0; i<len(b); i++{
b[i] = rand.Intn(1000)// 产生一个0到1000随机数
}
sum := sumarr(b)
fmt.Printf("sum = %d", sum)
}
func sumarr (a [10]int)(int){
sum := 0
for i:=0; i<len(a); i++{
sum = a[i]
}
return sum
}
冒泡排序
求最大小值,我遇到的,就是 for + if ,遇到最大/最小的,就留下
原理
- n个数,循环n-1次,设 i ∈[0, n-1]
- 第 i 次循环,比其的范围是[0, n-i-1],设 j ,j∈[0, n-i-1],每比一次,都可以把最大/最小的数,放到第i次比的范围的最右边,每比一次,范围缩小一个数( i + j = n-1)
代码实现
比如用随机数造一个元素个数为10的数组
package main
import (
"fmt"
"math/rand"
"time"
)
func main(){
a := [10]int{}
n := len(a)
rand.Seed(time.Now().UnixNano())
for k,_ := range a{
a[k] = rand.Intn(100)
}
for i:=0; i<n-1; i++{
for j:=0; j<n-1-i;j++{
if a[j]>a[j+1]{//从小到达和从大到小,在这改变一下大小于号就行
a[j],a[j+1] = a[j+1], a[j]
}
}
}
fmt.Println(a)
}
给一数组a,给一常数b,要求在数组a中找两个元素之和 = b,算出下标
package main
import "fmt"
// 找出数组中和为给定值的两个元素的下标,例如数组[1,3,5,8,7],
// 找出两个元素之和等于8的下标分别是(0,4)和(1,2)
// 求元素和,是给定的值
func myTest(a [5]int, target int) {
// 遍历数组
for i := 0; i < len(a); i++ {
other := target - a[i]
// 继续遍历
for j := i + 1; j < len(a); j++ {
if a[j] == other {
fmt.Printf("(%d,%d)\n", i, j)
}
}
}
}
func main() {
b := [5]int{1, 3, 5, 8, 7}
myTest(b, 8)
}