switch、select
分类 | 数据类型 | 占用字节 | 比特位 | 最大值 | 用途 |
---|---|---|---|---|---|
有符号整型 | int | C | D | E | F |
无符号整型 | uint | C | D | E | F |
单精度浮点型 | float32 | C | D | E | F |
单精度浮点型 | float64 | C | D | E | F |
ACSII字符 | byte | 1 | 8 | 255 | 表示 ACSII 表中的一个字符,和 uint8 类型本质上没有区别。不支持中文 |
Unicode字符 | rune | 3 | 32 | 表示一个 Unicode字符,和 int32 本质上也没有区别。类型本质上没有区别。支持中文 | |
字符串 | B | C | D | E | 本质是一个 byte 数组, uft-8 编码。英文字母占 1 个字节,中文字符占用 3 个字节 |
数组 | array | C | D | E | 数组的长度是固定的,所以在Go语言中很少直接使用数组 声明是需要指定长度 |
切片 | Slice | C | D | E | 切片是对数组的一个连续片段【左闭右开】的引用(可以容纳若干类型相同的元素的容器) 无法确定其值的长度,声明时可不指定长度 切片的第三个数,影响的只是切片的容量,而不会影响长度 |
字典 | map | C | D | E | 由若干个 key:value 这样的键值对映射组合在一起的数据结构,它是哈希表的一个实现,key唯一 key 不能是切片,不能是字典,不能是函数 在声明字典时,必须指定好你的key和value是什么类型 |
布尔类型 | boot | C | D | E | true 和 false |
指针 | pointer(ptr) | C | D | E | 内存地址 |
若你在64位的系统下,int 和 uint 都占用 8个字节,也就是64位。
科学计数法: aEb = a × 10b
浮点数 | 占用字节 | 位数 | 符号位 | 指数位 | 尾数位 | 最小值 | 精度 | 最大值 |
---|---|---|---|---|---|---|---|---|
float32 | 4 | 32 | 1 | 8 | 23 | 2-23 ≈ 1.19*10-7 | 6 | math.MaxFloat32 ≈ 3.4e38 |
float64 | 8 | 64 | 1 | 11 | 52 | 2-52 ≈ 2.22*10-16 | 15 | math.MaxFloat64 ≈ 3.4e308 |
package main
import "fmt"
var myfloat01 float32 = 100000182
var myfloat02 float32 = 100000187
var myfloat03 float32 = 10000018
var myfloat04 float32 = 10000023
func main() {
fmt.Println("myfloat01: ", myfloat01)
fmt.Println("myfloat02: ", myfloat02)
// 因为 float32 只有6位精度,只保证小数点后第7位计算结果精确,第8位开始回不精确
fmt.Println(myfloat02 == myfloat01+5)
fmt.Println("myfloat03 = ", myfloat03)
fmt.Println("myfloat04 = ", myfloat04)
fmt.Println(myfloat04 == myfloat04+5)
}
// 输出结果
// myfloat01 = 1.00000184e+08
// myfloat02 = 1.00000184e+08
// false
// myfloat03 = 1.0000018e+07
// myfloat04 = 1.0000023e+07
// true
强类型语言 true、false 和 0、1 不能直接比较
变量/常量都只能声明一次,声明多次,编译就会报错
单引号 双引号 不是等价的:单引号用来表示字符,双引号用来表示字符串 string
使用反引 `` 号包裹的字符串会忽略里面的转义,双引号不会
go支持定义一个类型字面量,也就是别名类型。
witch-case 是顺序执行的,select-case不是顺序执行的,都要写default
匿名变量,也称作占位符,或者空白标识符,用下划线表示。优点有三:
// Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据【nil 和 0 和 1 都不行】
if (age > 18 && gender == "male") {
分支 1
// if 里可以允许先运行一个表达式,取得变量后
} else if age := 20;age > 18 {
fmt.Println("已经成年了")
// else if (或 else)和 两边的花括号,必须在同一行
} else {
分支 else
}
switch month {
// case 后可以接多个多个条件,多个条件之间是 或 的关系,用逗号相隔。
case 3, 4, 5:
fmt.Println("春天")
// case 条件常量不能重复,否则 在编译时会报错: duplicate case "value" in switch
case 6, 7, 8:
fmt.Println("夏天")
case 9, 10, 11:
fmt.Println("秋天")
case 12, 1, 2:
fmt.Println("冬天")
default:
fmt.Println("输入有误...")
}
// switch 可不接表达式, 就相当于 if - elseif - else
score := 30
switch {
case score >= 95 && score <= 100:
fmt.Println("优秀")
// case 使用关键字 fallthrough 开启穿透能力
fallthrough
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("合格")
case score >= 0:
fmt.Println("不合格")
default:
fmt.Println("输入有误...")
}
// 1 一个表达式:a := 1 for a <= 5 {do sth}
// 2 for i := 1; i <= 5; i++ {do sth}
// 3 for-range 遍历一个可迭代的对象 for key, value := range myarr {do sth}
// 4 无限循环
for [一个表达式 | ( init; condition; increment ) | Range表达式 | 不接表达式]
{
statement(s);
}
// goto 可以打破原有代码执行顺序,直接跳转到某一行执行代码
// 通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能
// goto语句与标签之间不能有变量声明,否则编译错误
if condition {
do sth A
goto flag1;
do sth B
}
flag1: 表达式;
do sth C
// defer 能实现将这个 xxx 函数的调用延迟到当前函数执行完后再执行
func xxx() {
fmt.Println("B")
}
func main() {
defer xxx()
fmt.Println("A")
}
// 输出: A B
// 使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响
func main() {
name := "go"
defer fmt.Println(name) // 输出: go
name = "python"
fmt.Println(name) // 输出: python
}
// 输出: python go
// 如果 defer 后面跟的是匿名函数,情况会有所不同, defer 会取到最后的变量值
func main() {
name := "go"
fmt.Println(name) // 输出: go
defer func() {
fmt.Println(name) // 输出: python
}()
name = "python"
fmt.Println(name) // 输出: python
}
// 多个defer 反序调用,有点类似栈一样,后进先出。
func main() {
name := "go"
defer fmt.Println(name) // 输出: go
name = "python"
defer fmt.Println(name) // 输出: python
name = "java"
fmt.Println(name)
}
// 输出 java python go
// defer 是return 后才调用, 用途:当一个函数里有多个 return 时,你得多调用好多次这个函数,代码就臃肿起来了。
import "fmt"
var name string = "go"
func myfunc() string {
defer func() {
name = "python"
}()
fmt.Printf("myfunc 函数里的name:%s\n", name)
return name
}
func main() {
myname := myfunc()
fmt.Printf("main 函数里的name: %s\n", name)
fmt.Println("main 函数里的myname: ", myname)
}
// 输出如下
// myfunc 函数里的name:go
// main 函数里的name: python
// main 函数里的myname: go
跟 switch-case 相比,select-case 用法比较单一,它仅能用于 信道/通道 的相关操作。
package main
import (
"fmt"
)
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
c2 <- "hello"
// 在运行 select 时,会遍历所有(如果有机会的话)的 case 表达式,只要有一个信道有接收到数据,那么 select 就结束,所以输出如下
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
// 不写default 可能导致死锁
default:
fmt.Println("No data received.")
}
}
select VS switch
func main() { panic("crash") }
### recover:捕获异常,恢复程序或做收尾工作
recover 它可以让程序在发生宕机后起生回生。必须在 defer 函数中才能生效,其他作用域下,它是不工作的。
* 子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的
```go
import "fmt"
func set_data(x int) {
defer func() {
// recover() 可以将捕获到的panic信息打印
if err := recover(); err != nil {
fmt.Println(err)
}
}()
// 故意制造数组越界,触发 panic
var arr [10]int
arr[x] = 88
}
func main() {
set_data(20)
// 如果能执行到这句,说明panic被捕获了
// 后续的程序能继续运行
fmt.Println("everything is ok")
}
隐式语句块: