Go 基础数据结构与流程控制
Go 命令
-go build main.go
-go run main.go
-go install main.go
Go语言基础语法
-文件第一行:packae关键字声明包名
-如果要编译可执行文件,必须要有main包合main函数(入口函数),它没有参数也没有返回值
-单行注释 //
-多行注释 /* */
Go语言函数外的语句必须以关键字开头
函数内部定义的变量必须使用
变量
3种声明方式:
var name1 string
var name2 = "hina"
- 函数的内部专属:
name3 := "hina"
匿名变量(哑元变量):
当有些数据必须用变量接收但是又不是用它时,就可以用_来接收这个值
常量
const pi = 3.1415926
const UserNotExistErr = 10000
iota:实现枚举
两个要点:
iota
在const关键字出现时将被重置为0- const中每新增一行常量声明,iota累加1
流程控制
if 判断
var age = 19
if age>18{
fmt.Println("成年了")
}else if age>7{
fmt.Println("上过小学")
}else{
fmt.Println("快乐时光")
}
for 循环
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 九九乘法表
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%dx%d=%d\t", j, i, i*j)
}
fmt.Println()
}
// 当i=5时跳出for循环
// for i := 0; i < 10; i++ {
// if i == 5 {
// break
// }
// fmt.Println(i)
// }
// fmt.Println("over")
// 当i=5时跳过此次for循环
for i := 0; i < 10; i++ {
if i == 5 {
continue
}
fmt.Println(i)
}
fmt.Println("over")
基本数据类型
整型
无符号整型:uint8
,uint16
,uint32
,uint64
带符号整型:int8
,int16
,int32
,int64
int
:具体是32位还是64位看操作系统
uintptr
:表示指针
// 定义二进制数
var n1 = 0b011
fmt.Println(n1)
fmt.Printf("%b", n1)
// 定义八进制数
var n2 = 011
fmt.Println(n2)
fmt.Printf("%o", n2)
// 定义十六进制数
var n3 = 0xf1
fmt.Println(n3)
fmt.Printf("%x", n3)
浮点型
float64
和float32
Go语言中浮点数默认是float64
复数
complex128和complex64
布尔值
true
和false
不能和其他类型做转换
字符串
常用方法
字符串不能修改
byte和rune类型
都属于类型别名
字符串、字符、字节都是什么?
字符串:双引号包裹的是字符串
字符:单引号包裹的是字符,单个字母、符号、文字
字节:1byte=8bit
Go语言中字符串都是UTF-8编码
今日内容
for循环
package main
import (
"fmt"
)
// switch 简化大量的判断(一个变量和具体的值作比较)
func main() {
// var n = 8
// if n == 1 {
// fmt.Println("x1")
// } else if n == 2 {
// fmt.Println("x2")
// } else if n == 3 {
// fmt.Println("x3")
// } else if n == 4 {
// fmt.Println("x4")
// } else if n == 5 {
// fmt.Println("x5")
// } else {
// fmt.Println("nowork")
// }
// switch简化上面的代码
// switch n := 3; n {
// case 1:
// fmt.Println("x1")
// case 2:
// fmt.Println("x2")
// case 3:
// fmt.Println("x3")
// case 4:
// fmt.Println("x4")
// case 5:
// fmt.Println("x5")
// default:
// fmt.Println("nowork")
// }
// switch n := 7; n {
// case 1, 3, 5, 7, 9:
// fmt.Println("奇数")
// case 2, 4, 6, 8, 0:
// fmt.Println("偶数")
// default:
// fmt.Println(n)
// }
// age := 30
// switch {
// case age < 18:
// fmt.Print("未成年")
// case age >= 18 && age <= 30:
// fmt.Println("青年")
// case age > 35:
// fmt.Println("中年")
// default:
// fmt.Println("其他")
// }
// fallthrough
// s := "a"
// switch {
// case s == "a":
// fmt.Println("yes")
// fallthrough
// case s == "b":
// fmt.Println("bno")
// default:
// fmt.Println("...")
// }
// var flag = false
// for i := 0; i < 10; i++ {
// for j := 'A'; j < 'Z'; j++ {
// if j == 'C' {
// // flag = true
// i = 10
// break // 跳出内层for循环
// }
// fmt.Printf("%v-%c\n", i, j)
// }
// if flag {
// break // 跳出外层for循环
// }
// }
// goto + label 实现跳出for循环
for i := 0; i < 10; i++ {
for j := 'A'; j < 'Z'; j++ {
if j == 'C' {
goto Flag // 跳到指定的标签
}
fmt.Printf("%v-%c\n", i, j)
}
}
Flag: // label 标签
fmt.Println("over")
}
运算符
package main
import (
"fmt"
)
// 运算符
func main() {
a := 5
b := 2
a++ // 单独的语句 a = a + 1
b-- // b = b - 1
// 关系运算符
fmt.Println(a == b) // Go语言是强类型,相同类型的变量才能比较
// age := 30
// if age > 18 && age < 60 {
// fmt.Println("青年")
// } else {
// fmt.Println("非人哉")
// }
// 逻辑运算符
// &&:且,||:或者, !:取反,
// 位运算符 针对的是二进制数
// 5的二进制数表示: 101
// 2的二进制数表示: 010
// &:按位与(二进制数两个都为1才为1)
fmt.Println(5 & 2) // 000
// |:按位或(二进制数两个有一个为1就为1)
fmt.Println(5 | 2) // 111
// ^:按位异或(二进制数不一样则为1)
fmt.Println(5 ^ 2) // 111
// <<:将二进制位左移指定位数
fmt.Println(5 << 1) // 1010
fmt.Println(1 << 10) // 10000000000 => 1024
fmt.Println(5 >> 1) // 10
// var m = int8(1) // 只能存8位二进制数
// fmt.Printf(m << 10)
// 赋值运算符,用来给变量赋值的
x := 10
x <<= 2 // x = x << 2
x &= 2 // x = x & 2
x |= 2 // x = x | 2
x ^= 2 // x = x ^ 2
x >>= 2 // x = x >> 2
}
复合数据类型
数组 Array
package main
import "fmt"
// 复合数据类型
// 数组
// 存放元素的容器,必须指定存放的元素的类型和容量(长度)
func main() {
// 数组的长度时数组类型的一部分,长度不一样不可比较
// var a1 [3]bool // [true false true]
// var a2 [4]bool // [true false true false]
// fmt.Printf("a1:%T, a2:%T\n", a1, a2)
// // 数组的初始化
// // 如果不出是花:默认元素都是零值(布尔值:false,整型和浮点型都是0,字符串:"")
// fmt.Println(a1, a2)
// // 1.初始化方法1
// a1 = [3]bool{true, true, true}
// fmt.Println(a1)
// // 2.初始化方法2:根据初始值自动退管数组的长度时多少
// ax := [...]int{1, 2, 3, 4, 5, 6, 7}
// fmt.Println(ax)
// // 3.初始化方式3:根据索引来初始化
// a3 := [5]int{0: 1, 4: 2}
// fmt.Println(a3)
// 数组的遍历
// cities := [...]string{"BJ", "SH", "SZ"}
// // 1.根据所有遍历
// for i := 0; i < len(cities); i++ {
// fmt.Println(cities[i])
// }
// // 2.for range 遍历
// for i, v := range cities {
// fmt.Println(i, v)
// }
// 多维数组 [ [1 2] [3 4] [5 6]]
// var a11 [3][2]int
// a11 := [3][2]int{
// {1, 2},
// {3, 4},
// {5, 6},
// }
// fmt.Println(a11)
// 多维数组的遍历
// for _, v1 := range a11 {
// fmt.Println(v1)
// for _, v2 := range v1 {
// fmt.Println(v2)
// }
// }
// 数组是值类型
// b1 := [3]int{1, 2, 3}
// b2 := b1 // 深拷贝
// b2[0] = 100
// fmt.Println(b1, b2)
// 练习求数组[1, 3, 5, 6, 8]所有元素的和
arr := [...]int{1, 3, 5, 7, 8}
// sum := 0
// for _, v := range arr {
// sum += v
// }
// fmt.Println(sum)
// 找出数组中和为指定值得两个元素的下标,如从[1,3,5,7,8]找出和为8的两个元素的下标
for i, v := range arr {
for j := i + 1; j < len(arr)-i; j++ {
if v+arr[j] == 8 {
fmt.Println(i, j)
}
}
}
}
切片
切片是指向了一个底层的数组
切片的长度就是它元素的个数
切片的容量是底层数组从切片的第一个元素到最后一个元素的数量
切片与数组的区别
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。
数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。
切片的本质
切片的本质就是一个框,框住了一块连续的内存。
切片属于引用类型,真正的数据都是保存在底层数组里的。
要判断一个切片是否为空的,要用len(s)==0
来判断
package main
import "fmt"
// 切片slice
func main() {
// 切片的定义
// var s1 []int // 定义一个存放int类型元素的切片
// var s2 []string // 定义了一个存放string类型元素的切片
// fmt.Println(s1, s2)
// fmt.Println(s1 == nil) // true nil 表示没有开辟内存空间
// fmt.Println(s2 == nil)
// 初始化
// s1 = []int{1, 2, 3}
// s2 = []string{"hina", "lem", "阳菜"}
// fmt.Println(s1, s2)
// fmt.Println(s1 == nil)
// fmt.Println(s2 == nil)
// 长度和容量
// fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1))
// fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2))
// // 2.由数组得到切片
// a1 := [...]int{1, 3, 4, 5, 6, 7}
// // s3 := a1[1:4]
// // fmt.Println(s3)
// s5 := a1[:4] // [1 3 4 5]
// s6 := a1[4:] // [6 7]
// fmt.Println(s5)
// fmt.Println(s6)
// fmt.Println(a1)
// // fmt.Println(a1[1:])
// // 切片的容量是指底层数组的容量
// fmt.Printf("len(s5):%d cap(s5):%d\n", len(s5), cap(s5)) // 4, 6
// // 底层数组从切片的第一个元素到最后的元素数量
// fmt.Printf("len(s6):%d cap(s6):%d\n", len(s6), cap(s6)) // 2, 2
// // 切片再切片
// s8 := s5[2:] // [4 5]
// fmt.Printf("len(s8):%d cap(s8):%d\n", len(s8), cap(s8)) // 2, 2
// 切片是引用类型,都是指向了底层的一个数组
// fmt.Println("s6", s6)
// a1[4] = 111 // 修改了底层数组的值
// a1[5] = 222
// a1[2] = 333
// fmt.Println("s6", s6)
// fmt.Println("s5", s5)
// 使用make()函数创建切片
// ss1 := make([]int, 5, 10) // 5是长度,10是容量,不写10默认和长度一样
// fmt.Println(ss1, len(ss1), cap(ss1))
// ss2 := make([]int, 0, 10) // 5是长度,10是容量,不写10默认和长度一样
// fmt.Println(ss2, len(ss2), cap(ss2))
// 切片的赋值
s3 := []int{1, 3, 5}
s4 := s3 // s3和s4都指向了用一个底层数组,切片的本质就是一个框
fmt.Println(s3, s4)
s3[0] = 1000
fmt.Println(s3, s4)
// 切片的遍历
// 1.索引遍历
for i := 0; i < len(s3); i++ {
fmt.Println(s3[i])
}
// 2.for range循环
for i, v := range s3 {
fmt.Println(i, v)
}
}
切片的扩容
package main
import "fmt"
// append()为切片追加元素
func main() {
// var a int = 10
// //每个变量都有两层含义,变量的内存和变量的地址
// fmt.Printf("a = %d\n", a) //变量的内存 10
// fmt.Printf("a = %v\n", &a) //变量的地址 0xc042060080
s1 := []string{"lem", "hina", "rui"}
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
// 调用append函数必须用原来的变量来接收返回值
s1 = append(s1, "emt") // append追加元素,原来的底层数组放不下时,Go底层就会换一个内存地址去存数据
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
s1 = append(s1, "xx", "yy")
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
ss := []string{"aa", "bb", "cc"}
s1 = append(s1, ss...)
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
}
append
package main
import (
"fmt"
)
// copy
func main() {
ia := [...]int{1, 2, 3, 4, 5}
ia2 := ia[1:3]
ia[2] = 1000
fmt.Printf("%p, %v\n", &ia, ia)
ia2 = append(ia2, 7, 8)
fmt.Printf("%p, %v\n", &ia, ia)
fmt.Printf("%p, %v\n", &ia2, ia2)
fmt.Println("===============================")
a := [...]int{1, 2, 3, 4, 5}
a2 := a[1:3]
fmt.Printf("%p, %v\n", &a, a)
a2 = append(a2, 7, 8, 9, 10)
fmt.Printf("%p, %v\n", &a, a)
fmt.Printf("%p, %v\n", &a2, a2)
}
>>>>>>>>
0xc00000c360, [1 2 1000 4 5]
0xc00000c360, [1 2 1000 7 8]
0xc000004078, [2 1000 7 8]
===============================
0xc00000c3f0, [1 2 3 4 5]
0xc00000c3f0, [1 2 3 4 5]
0xc0000040a8, [2 3 7 8 9 10]
copy
package main
import (
"fmt"
"sort"
)
// copy
func main() {
// a1 := []int{1, 3, 5}
// a2 := a1 // 赋值 a1,a2指向的是同一个内存地址
// // var a3 []int // nil没有内存空间
// var a3 = make([]int, 3, 3)
// copy(a3, a1) // copy a3是新开辟一块内存将原来的数据复制过来
// fmt.Println(a1, a2, a3)
// a1[0] = 100
// fmt.Println(a1, a2, a3)
// // 将a1中索引为1的3这个元素删除
// a1 = append(a1[:1], a1[2:]...)
// fmt.Println(a1, cap(a1))
// x1 := [...]int{1, 3, 5} // 数组
// s1 := x1[:] // 切片
// fmt.Println(s1, len(s1), cap(s1))
// 1.切片不保存具体的值
// 2.切片对应一个底层数据
// 3.底层数组都是占用一块连续的内存
// s1 = append(s1[:1], s1[2:]...)
// fmt.Println(s1, len(s1), cap(s1))
// fmt.Println(x1)
// 切片的练习
var a = make([]int, 5, 10) // 创建切片 长度为5,容量为10
fmt.Println(a)
for i := 0; i < 10; i++ {
a = append(a, i)
}
fmt.Println(a)
a1 := [...]int{3, 5, 7, 1, 2}
sort.Ints(a1[:]) // 对切片进行排序
fmt.Println(a1)
}
指针
Go语言中不存在指针操作,只需要记住两个符号
&
:取地址*
:根据地址取值
package main
import (
"fmt"
)
// 指针
func main() {
// 1.&:取地址
// n := 18
// p := &n
// fmt.Println(p)
// fmt.Printf("%T\n", p) // *int表示int类型的指针
// // 2.*:根据变量取值
// m := *p
// fmt.Println(m)
}
new函数
package main
import (
"fmt"
)
// 指针
func main() {
var a *int // nil 空指针
fmt.Println(a)
// *a = 100 // 空的指针是找不到值得
// fmt.Println(*a)
// new函数申请一个内存地址
var a1 = new(int)
fmt.Println(*a1)
*a1 = 100
fmt.Println(a1)
fmt.Println(*a1)
}
>>>>>>>>
<nil>
0
0xc0000140e0
100
new和make的区别
- make和new都是用来申请内存的
- new很少用,一般用来给基本数据类型申请内存,
string
、int
,返回的是对于类型的指针 - make是用来给
slice
,map
,channel
申请内存的,make函数返回的是对于的者三个类型本身
map
package main
import (
"fmt"
)
// map
func main() {
var m1 map[string]int
fmt.Println(m1) // 还没有初始化,还没有在内存中开辟空间
m1 = make(map[string]int, 10) // 要估算号该map容量,避免在程序运训期间再动态扩容
fmt.Println(m1)
m1["hina"] = 1111
m1["lem"] = 2222
m1["lem1"] = 22221
fmt.Println(m1)
fmt.Println(m1["hina"])
fmt.Println(m1["hinaxx"]) // 如果不存在这个key拿到对应类型的0值
val, ok := m1["xxx"]
if !ok {
fmt.Println("查无此key")
} else {
fmt.Println(val)
}
// map的遍历
for k, v := range m1 {
fmt.Println(k, v)
}
// 只遍历key
for k := range m1 {
fmt.Println(k)
}
// 只遍历value
for _, v := range m1 {
fmt.Println(v)
}
// 删除
delete(m1, "lem1")
fmt.Println(m1)
// 删除不存在的key,则不操作
}
对于map类型中按照key排序在输出
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
// map
func main() {
rand.Seed(time.Now().UnixNano()) // 初始化随机数种子
var scoremap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) // 生成stu开头的字符串
value := rand.Intn(100) // 生成0-99随即证书
scoremap[key] = value
}
fmt.Println(scoremap)
// 取出map中所有key存入切片sl
s1 := make([]string, 0, 200)
for key := range scoremap {
s1 = append(s1, key)
}
fmt.Println(s1)
// 对切片进行排序
sort.Strings(s1)
fmt.Println(s1)
// 按照排序后的key遍历map
for _, key := range s1 {
fmt.Println(key, scoremap[key])
}
}
map和slice组合
package main
import "fmt"
// map和slice组合
// 切记不管是哪种组合都需要初始化两个类型
func main() {
// 元素类型为map的切片
var s1 = make([]map[int]string, 10, 10)
// s1[0][100] = "A" // 没有对内部的map做初始化
s1[0] = make(map[int]string, 10)
s1[0][0] = "HINA"
fmt.Println(s1)
// 值为切片类型的map
var m1 = make(map[string][]int, 10)
m1["北京"] = []int{10, 20, 30}
fmt.Println(m1)
}
函数
package main
import (
"fmt"
)
// 函数
// 函数的定义
func sum(x int, y int) (res int) {
return x + y
}
// 函数没有返回值
func f1(x int, y int) {
fmt.Println(x + y)
}
// 没有参数没有返回值
func f2() {
fmt.Println("f2")
}
// 没有参数有返回值
func f3() int {
return 111
}
// 返回值可以命名也可以不命名
// 命名的返回值就相当于在函数中声明一个变量
func f4(x int, y int) (ret int) {
ret = x + y
return // 使用命名返回值return后面可以省略
}
// 多个返回值
func f5() (int, string) {
return 11, "HINA"
}
// 参数的类型简写:当参数中连续多个参数类型一致时,我们可以将前面那个参数的类型省略
func f6(x, y int) int {
return x + y
}
// 可变长参数,必须放在函数参数最后
func f7(x string, y ...int) {
fmt.Println(x, y) // y的类型是切片 []int
}
// Go语言中没有默认参数这个概念
func main() {
res := sum(10, 20)
fmt.Println(res)
_, n := f5()
fmt.Println(n)
f7("hina")
f7("hina", 1, 2, 3, 4, 6)
}