基础数据类型
- 整型,用于表示整数
- 浮点型,用于表示小数
- 复数,用来表示复数
- 布尔型,用于表示真/假
- 字符串,用于表示文本信息
复合数据类型
- 数组,用于表示多个数据(数据集合)
- 结构体,用于自定义一些数据集合和方法
引用数据类型
- 指针,用于表示内存地址的类型
- 切片,用于表示多个数据(数据集合)
- 字典,用于表示键值对集合
- 函数,用于将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用
- 通道,channel,是goroutine中的通信机制
接口数据类型
- 接口,用于约束和泛指数据类型
上一节我们讨论了基本数据类型,它们可以用于构建程序中数据的结构,是Go语言世界的原子。在接下来的几节,我们主要讨论四种类型:数组、slice、map和结构体。同时,我们将演示如何使用结构体来解码和编码到对应JSON格式的数据,并且通过结合使用模板来生成HTML页面。
数组和结构体是复合(聚合)类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成——每个数组元素都是完全相同的类型——结构体则是由异构的元素组成的。数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。
一、数组的定义
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组。和数组对应的类型是Slice(切片),它是可以增长和收缩的动态序列,slice功能也更灵活,但是要理解slice工作原理的话需要先理解数组。
数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len函数将返回数组中元素的个数。
Golang Array的特点:
数组:是同一种数据类型的固定长度的序列。
数组定义:
var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。长度是数组类型的一部分,因此,
var a[5] int和var a[10]int是不同的类型。数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:
len-1for i := 0; i < len(a); i++ { } for index, v := range a { }访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
支持 “==”、”!=” 操作符,因为内存总是被初始化过的
指针数组 [n]*T,数组指针 *[n]T。
二、数组的初始化
一维数组
// 方式一:先声明再赋值(声明时内存中已开辟空间,内存初始化的值是0)
var numbers [3]int
numbers[0] = 999
numbers[1] = 666
numbers[2] = 333
// 方式二:声明+赋值
var names = [2]string{"乔丹","科比"}
// 方式三:声明+赋值 + 指定位置
var ages = [3]int{0:87,1:73,2:99}
// 方式四:省略个数
var names = [...]string{"刘备","诸葛亮"}
var ages = [...]int{0:87, 2:99}
// 声明 指针类型的数组(指针类型),不会开辟内存初始化数组中的值,numbers = nil
var numbers *[3]int
// 声明数组并初始化,返回的是 指针类型的数组(指针类型)
numbers := new([3]int)
代码:
package main
import (
"fmt"
)
var arr0 [5]int = [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"}
func main() {
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}, // 别忘了最后一行的逗号。
}
fmt.Println(arr0, arr1, arr2, str)
fmt.Println(a, b, c, d)
}
输出结果:
[1 2 3 0 0] [1 2 3 4 5] [1 2 3 4 5 6] [ hello world tom]
[1 2 0] [1 2 3 4] [0 0 100 0 200] [{user1 10} {user2 20}]
多维数组
示例1
package main
import "fmt"
func main() {
var nestData1 [3]int
fmt.Println(nestData1) // [0 0 0]
var nestData2 [3][2][3]int
fmt.Println(nestData2) // [[[-1 0 0] [0 0 0]] [[0 0 0] [0 0 0]] [[0 0 0] [0 0 0]]]
//[ [0,0,0],[0,0,0] ]
var nestData3 [2][3]int
nestData3[0] = [3]int{11, 22, 33}
nestData3[1][1] = 666
fmt.Println(nestData3) // [[11 22 33] [0 666 0]]
nestData4 := [2][3]int{[3]int{11, 22, 33}, [3]int{44, 55, 66}}
fmt.Println(nestData4) // [[11 22 33] [44 55 66]]
}
示例2
package main
import (
"fmt"
)
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
func main() {
a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。
fmt.Println(arr0, arr1)
fmt.Println(a, b)
}
输出结果:
[[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]
[[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]
值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。
package main
import (
"fmt"
)
func test(x [2]int) {
fmt.Printf("x: %p\n", &x)
x[1] = 1000
}
func main() {
a := [2]int{}
fmt.Printf("a: %p\n", &a)
test(a)
fmt.Println(a)
}
输出结果:
a: 0xc42007c010
x: 0xc42007c030
[0 0]
内置函数 len 和 cap 都返回数组长度 (元素数量)。
package main
func main() {
a := [2]int{}
println(len(a), cap(a))
}
输出结果:
2 2
多维数组遍历
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()
}
}
输出结果:
(0,0)=1 (0,1)=2 (0,2)=3
(1,0)=7 (1,1)=8 (1,2)=9
三、数组内存管理

数组,定长且元素类型一致的数据集合。
必备知识点:
数组的内存是连续的。
数组的内存地址实际上就是数组第一个元素的内存地址。
每个字符串的内部存储:
len+strtype stringStruct struct { str unsafe.Pointer len int }
示例1:
package main
import "fmt"
func main() {
nums := [3]int8{11, 22, 33}
fmt.Printf("数组的内存地址:%p \n", &nums)
fmt.Printf("数组第1个元素的内存地址:%p \n", &nums[0])
fmt.Printf("数组第2个元素的内存地址:%p \n", &nums[1])
fmt.Printf("数组第3个元素的内存地址:%p \n", &nums[2])
}
>>> 输出
数组的内存地址:0xc00001604a
数组第1个元素的内存地址:0xc00001604a
数组第2个元素的内存地址:0xc00001604b
数组第3个元素的内存地址:0xc00001604c
示例2:
package main
import "fmt"
func main() {
nums := [3]int32{11, 22, 33}
fmt.Printf("数组的内存地址:%p \n", &nums)
fmt.Printf("数组第1个元素的内存地址:%p \n", &nums[0])
fmt.Printf("数组第2个元素的内存地址:%p \n", &nums[1])
fmt.Printf("数组第3个元素的内存地址:%p \n", &nums[2])
}
>>> 输出
数组的内存地址:0xc0000b4004
数组第1个元素的内存地址:0xc0000b4004
数组第2个元素的内存地址:0xc0000b4008
数组第3个元素的内存地址:0xc0000b400c
示例3:
package main
import "fmt"
func main() {
names := [2]string{"乔丹", "科比"}
fmt.Printf("数组的内存地址:%p \n", &names)
fmt.Printf("数组第1个元素的内存地址:%p \n", &names[0])
fmt.Printf("数组第2个元素的内存地址:%p \n", &names[1])
}
>>> 输出:
数组的内存地址:0xc000128020
数组第1个元素的内存地址:0xc000128020
数组第2个元素的内存地址:0xc000128030
四、可变和拷贝
可变,数组的元素可以被更改(长度和类型都不可以修改)。
names := [2]string{"乔丹", "科比"}
names[1] = "詹姆斯"
注意:字符串不可以被修改。
拷贝,变量赋值时重新拷贝一份。
name1 := [2]string{"乔丹", "科比"}
name2 := name1
name1[1] = "詹姆斯"
fmt.Println(name1,name2) // [乔丹 科比] [詹姆斯 科比]
拷贝示例
package main
import "fmt"
func printArr(arr [5]int) {
arr[0] = 10
for i, v := range arr {
fmt.Println(i, v)
}
}
func printArr2(arr *[5]int) {
arr[0] = 10
for i, v := range arr {
fmt.Println(i, v)
}
}
func main() {
var arr1 [5]int
printArr(arr1)
fmt.Println(arr1) // 值拷贝
arr2 := [...]int{2, 4, 6, 8, 10}
printArr2(&arr2)
fmt.Println(arr2) // 引用拷贝
}
输出
0 10
1 0
2 0
3 0
4 0
[0 0 0 0 0]
0 10
1 4
2 6
3 8
4 10
[10 4 6 8 10]
五、长度索引切片和循环
package main
import "fmt"
func main() {
// 1. 长度
//name := [2]string{"乔丹", "科比"}
//fmt.Println(len(name))
// 2. 索引
//name := [2]string{"乔丹", "科比"}
//data := name[0]
//fmt.Println(data)
//name[0] = "eric"
//fmt.Println(name)
// 3. 切片
//nums := [3]int32{11, 22, 33}
//data := nums[0:2] // 获取 0 <= 下标 < 2
//fmt.Println(data)
// 4. 循环
//nums := [3]int32{11, 22, 33}
//for i:=0;i<len(nums);i++{
// fmt.Println(i, nums[i] )
//}
// 5.for range 循环
nums := [3]int32{11, 22, 33}
for key, item := range nums {
fmt.Println(key, item)
}
for key := range nums {
fmt.Println(key)
}
for _,item := range nums {
fmt.Println(item)
}
}
六、数组练习
练习1
求数组所有元素之和
参考代码
package main
import (
"fmt"
"math/rand"
"time"
)
// 求元素和
func sumArr(a [10]int) int {
var sum int = 0
for i := 0; i < len(a); i++ {
sum += a[i]
}
return sum
}
func main() {
// 若想做一个真正的随机数,要种子
// seed()种子默认是1
//rand.Seed(1)
rand.Seed(time.Now().Unix())
var b [10]int
for i := 0; i < len(b); i++ {
// 产生一个0到1000随机数
b[i] = rand.Intn(1000)
}
sum := sumArr(b)
fmt.Printf("sum=%d\n", sum)
}
练习2
找出数组中和为给定值的两个元素的下标,例如数组[1,3,5,8,7],找出两个元素之和等于8的下标分别是(0,4)和(1,2)
参考代码
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)
}