【Go学习】-01-2-常见范例及关键字

[复制链接]
发表于 2025-11-15 15:56:07 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

常见范例

数组

数组是一个由固定长度的特定范例元素构成的序列,一个数组可以由零个或多个元素构成。
由于数组的长度是固定的,以是在Go语言中很少直接使用数组。
Go语言数组的声明:
  1. var 数组变量名 [元素数量]Type
复制代码


  • 数组变量名:数组声明及使用时的变量名。
  • 元素数量:数组的元素数量,可以是一个表达式,但终极通过编译期盘算的效果必须是整型数值,元素数量不能含有到运行时才气确认巨细的数值。
  • Type:可以是恣意根本范例,包罗数组自己,范例为数组自己时,可以实现多维数组。
例子:
  1. //默认数组中的值是类型的默认值
  2. var arr [3]int
复制代码
取值:


  • 通过索引下标取值,索引从0开始
    1. fmt.Println(arr[0])
    2. fmt.Println(arr[1])
    3. fmt.Println(arr[2])
    复制代码
  • for range获取
    1. for index,value := range arr{
    2.         fmt.Printf("索引:%d,值:%d \n",index,value)
    3. }
    复制代码
赋值


  • 初始化的时间赋值
    1. var arr [3]int = [3]int{1,2,3}
    2. //如果第三个不赋值,就是默认值0
    3. var arr [3]int = [3]int{1,2}
    4. //可以使用简短声明
    5. arr := [3]int{1,2,3}
    6. //如果不写数据数量,而使用...,表示数组的长度是根据初始化值的个数来计算
    7. arr := [...]int{1,2,3}
    复制代码
  • 通过索引下标赋值
    1. var arr [3]int
    2. arr[0] = 5
    3. arr[1] = 6
    4. arr[2] = 7
    复制代码
   肯定要注意,数组是定长的,不可更改,在编译阶段就决定了
  小本事: 如果觉的每次写 [3]int 有点贫苦,你可以为 [3]int 界说一个新的范例。
  1. type arr3 [3]int
  2. //这样每次用arr3 代替[3]int,注意前面学过 定义一个类型后 arr3就是一个新的类型
  3. var arr arr3
  4. arr[0] = 2
  5. for index,value := range arr{
  6.     fmt.Printf("索引:%d,值:%d \n",index,value)
  7. }
复制代码
如果想要只初始化第三个值怎么写?
  1. //2 给索引为2的赋值 ,所以结果是 0,0,3
  2. arr := [3]int{2: 3}
  3. for index, value := range arr {
  4.     fmt.Printf("索引:%d,值:%d \n", index, value)
  5. }
复制代码
给索引值为2的数组的值赋为3
比力

如果两个数组范例雷同(包罗数组的长度,数组中元素的范例)的情况下,我们可以直接通过较运算符(==和!=)来判定两个数组是否相称,只有当两个数组的全部元素都是相称的时间数组才是相称的,不能比力两个范例差别的数组,否则步伐将无法完成编译。
  1. a := [2]int{1, 2}
  2. b := [...]int{1, 2}
  3. c := [2]int{1, 3}
  4. fmt.Println(a == b, a == c, b == c) // "true false false"
  5. d := [3]int{1, 2}
  6. fmt.Println(a == d) // 编译错误:无法比较 [2]int == [3]int
复制代码
多维数组

Go语言中答应使用多维数组,由于数组属于值范例,以是多维数组的全部维度都会在创建时自动初始化零值,多维数组尤其得当管理具有父子关系大概与坐标系干系联的数据。
声明多维数组的语法如下所示:
  1. //array_name 为数组的名字,array_type 为数组的类型,size1、size2 等等为数组每一维度的长度。
  2. var array_name [size1][size2]...[sizen] array_type
复制代码
二维数组是最简朴的多维数组,二维数组本质上是由多个一维数组构成的。
  1. // 声明一个二维整型数组,两个维度的长度分别是 4 和 2
  2. var array [4][2]int
  3. // 使用数组字面量来声明并初始化一个二维整型数组
  4. array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
  5. // 声明并初始化数组中索引为 1 和 3 的元素
  6. array = [4][2]int{1: {20, 21}, 3: {40, 41}}
  7. // 声明并初始化数组中指定的元素
  8. array = [4][2]int{1: {0: 20}, 3: {1: 41}}
复制代码
取值:

  • 通过索引下标取值
    1. fmt.Println(array[1][0])
    复制代码
  • 循环取值
    1. for index,value := range array{
    2.                 fmt.Printf("索引:%d,值:%d \n",index,value)
    3.         }
    复制代码
赋值:
  1. // 声明一个 2×2 的二维整型数组
  2. var array [2][2]int
  3. // 设置每个元素的整型值
  4. array[0][0] = 10
  5. array[0][1] = 20
  6. array[1][0] = 30
  7. array[1][1] = 40
复制代码
只要范例同等,就可以将多维数组相互赋值,如下所示,多维数组的范例包罗每一维度的长度以及存储在元素中数据的范例:
  1. // 声明两个二维整型数组 [2]int [2]int
  2. var array1 [2][2]int  
  3. var array2 [2][2]int
  4. // 为array2的每个元素赋值
  5. array2[0][0] = 10
  6. array2[0][1] = 20
  7. array2[1][0] = 30
  8. array2[1][1] = 40
  9. // 将 array2 的值复制给 array1
  10. array1 = array2
复制代码
由于数组中每个元素都是一个值,以是可以独立复制某个维度,如下所示:
  1. // 将 array1 的索引为 1 的维度复制到一个同类型的新数组里
  2. var array3 [2]int = array1[1]
  3. // 将数组中指定的整型值复制到新的整型变量里
  4. var value int = array1[1][0]
复制代码
切片

切片(Slice)与数组一样,也是可以容纳多少范例雷同的元素的容器。
与数组差别的是,无法通过切片范例来确定其值的长度。
每个切片值都会将数组作为其底层数据布局。
我们也把如许的数组称为切片的底层数组。
切片(slice)是对数组的一个连续片断的引用,以是切片是一个引用范例。
这个片断可以是整个数组,也可以是由起始和停止索引标识的一些项的子集,必要注意的是,停止索引标识的项不包罗在切片内(左闭右开的区间)。
Go语言中切片的内部布局包罗地点、巨细和容量,切片一样平常用于快速地利用一块数据聚集。
从连续内存地区天生切片是常见的利用,格式如下:
  1. slice [开始位置 : 结束位置]
复制代码
语法阐明如下:


  • slice:表现目的切片对象;
  • 开始位置:对应目的切片对象的索引;
  • 竣事位置:对应目的切片的竣事索引。
从数组天生切片,代码如下:
  1. var a  = [3]int{1, 2, 3}
  2. //a[1:2] 生成了一个新的切片
  3. fmt.Println(a, a[1:2])
复制代码
从数组或切片天生新的切片拥有如下特性:


  • 取出的元素数量为:竣事位置 - 开始位置;
  • 取出元素不包罗竣事位置对应的索引,切片末了一个元素使用 slice[len(slice)] 获取;
  • 当缺省开始位置时,表现从连续地区开头到竣事位置(a[:2]);
  • 当缺省竣事位置时,表现从开始位置到整个连续地区末端(a[0:]);
  • 两者同时缺省时,与切片自己等效(a[:]);
  • 两者同时为 0 时,等效于空切片,一样平常用于切片复位(a[0:0])。
注意:超界会报运行时错误,好比数组长度为3,则竣事位置最大只能为3
   切片在指针的底子上增长了巨细,束缚了切片对应的内存地区,切片使用中无法对切片内部的地点和巨细举行手动调解,因此切片比指针更安全、强盛。
  示例
   切片和数组密不可分,如果将数组明白为一栋办公楼,那么切片就是把差别的连续楼层出租给使用者,出租的过程必要选择开始楼层和竣事楼层,这个过程就会天生切片
  1. var highRiseBuilding [30]int
  2. for i := 0; i < 30; i++ {
  3.      highRiseBuilding[i] = i + 1
  4. }
  5. // 区间
  6. fmt.Println(highRiseBuilding[10:15])
  7. // 中间到尾部的所有元素
  8. fmt.Println(highRiseBuilding[20:])
  9. // 开头到中间指定位置的所有元素
  10. fmt.Println(highRiseBuilding[:2])
复制代码
声明切片

除了可以从原有的数组大概切片中天生切片外,也可以声明一个新的切片,每一种范例都可以拥有其切片范例,表现多个雷同范例元素的连续聚集。
切片范例声明格式如下:
  1. //name 表示切片的变量名,Type 表示切片对应的元素类型。
  2. var name []Type
复制代码
  1. // 声明字符串切片
  2. var strList []string
  3. // 声明整型切片
  4. var numList []int
  5. // 声明一个空切片
  6. var numListEmpty = []int{}
  7. // 输出3个切片
  8. fmt.Println(strList, numList, numListEmpty)
  9. // 输出3个切片大小
  10. fmt.Println(len(strList), len(numList), len(numListEmpty))
  11. // 切片判定空的结果
  12. fmt.Println(strList == nil)
  13. fmt.Println(numList == nil)
  14. fmt.Println(numListEmpty == nil)
复制代码
切片是动态布局,只能与 nil 判定相称,不能相互判定相称。声明新的切片后,可以使用 append() 函数向切片中添加元素。
  1. var strList []string
  2. // 追加一个元素
  3. strList = append(strList, "go教程")
  4. fmt.Println(strList)
复制代码
使用 make() 函数构造切片

如果必要动态地创建一个切片,可以使用 make() 内建函数,格式如下:
  1. make( []Type, size, cap )
复制代码
Type 是指切片的元素范例,size 指的是为这个范例分配多少个元素,cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,低落多次分配空间造成的性能题目。
  1. a := make([]int, 2)
  2. b := make([]int, 2, 10)
  3. fmt.Println(a, b)
  4. //容量不会影响当前的元素个数,因此 a 和 b 取 len 都是 2
  5. //但如果我们给a 追加一个 a的长度就会变为3
  6. fmt.Println(len(a), len(b))
复制代码
  使用 make() 函数天生的切片肯定发生了内存分配利用,但给定开始与竣事位置(包罗切片复位)的切片只是将新的切片布局指向已经分配好的内存地区,设定开始与竣事位置,不会发生内存分配利用。
  思考题 len和cap

  1. var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  2. myslice := numbers4[4:6]
  3. //这打印出来长度为2
  4. fmt.Printf("myslice为 %d, 其长度为: %d, 其容量为: %d\n", myslice, len(myslice), cap(myslice))
  5. //myslice为 [5 6], 其长度为: 2, 其容量为: 6
  6. myslice = myslice[:5]
  7. fmt.Printf("myslice为 %d, 其长度为: %d, 其容量为: %d\n", myslice, len(myslice), cap(myslice))
  8. //myslice为 [5 6 7 8 9], 其长度为: 5, 其容量为: 6
  9. //为什么 myslice 的长度为2,却能访问到第四个元素
  10. fmt.Printf("myslice的第四个元素为: %d", myslice[3])
  11. //myslice的第四个元素为: 8
复制代码
由于cap是实在索引到数组末端,以是自从myslice := numbers4[4:6],切片后,myslice的cap不绝为6,固然被len影响,但是仍旧可以访问到cap中的元素。
容量盘算规则:cap = 数组长度 - 切片起始索引
复制切片

Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果参加的两个数组切片不一样大,就会按照此中较小的谁人数组切片的元素个数举行复制。
copy() 函数的使用格式如下:
  1. copy( destSlice, srcSlice []T) int
复制代码
此中 srcSlice 为数据泉源切片,destSlice 为复制的目的(也就是将 srcSlice 复制到 destSlice),目的切片必须分配过空间且富足承载复制的元素个数,而且泉源和目的的范例必须同等,copy() 函数的返回值表现实际发生复制的元素个数。
下面的代码展示了使用 copy() 函数将一个切片复制到另一个切片的过程:
  1. slice1 := []int{1, 2, 3, 4, 5}
  2. slice2 := []int{5, 4, 3}
  3. copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
  4. copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
复制代码
切片的引用和复制操尴尬刁难切片元素的影响:
  1. package main
  2. import "fmt"
  3. func main() {
  4.     // 设置元素数量为1000
  5.     const elementCount = 1000
  6.     // 预分配足够多的元素切片
  7.     srcData := make([]int, elementCount)
  8.     // 将切片赋值
  9.     for i := 0; i < elementCount; i++ {
  10.         srcData[i] = i
  11.     }
  12.     // 引用切片数据 切片不会因为等号操作进行元素的复制
  13.     refData := srcData
  14.     // 预分配足够多的元素切片
  15.     copyData := make([]int, elementCount)
  16.     // 将数据复制到新的切片空间中
  17.     copy(copyData, srcData)
  18.     // 修改原始数据的第一个元素
  19.     srcData[0] = 999
  20.     // 打印引用切片的第一个元素 引用数据的第一个元素将会发生变化
  21.     fmt.Println(refData[0])
  22.     // 打印复制切片的第一个和最后一个元素 由于数据是复制的,因此不会发生变化。
  23.     fmt.Println(copyData[0], copyData[elementCount-1])
  24.     // 复制原始数据从4到6(不包含)
  25.     copy(copyData, srcData[4:6])
  26.     for i := 0; i < 5; i++ {
  27.         fmt.Printf("%d ", copyData[i])
  28.     }
  29. }
复制代码
refData是软链接,copyData是硬链接
map

map 是一种无序的键值对的聚集。
map 最紧张的一点是通过 key 来快速检索数据,key 雷同于索引,指向数据的值。
map 是一种聚集,以是我们可以像迭代数组和切片那样迭代它。不外,map 是无序的,我们无法决定它的返回次序,这是由于 map 是使用 hash 表来实现的。
map 是引用范例,可以使用如下方式声明:
  1. //[keytype] 和 valuetype 之间允许有空格。
  2. var mapname map[keytype]valuetype
复制代码
此中:


  • mapname 为 map 的变量名。
  • keytype 为键范例。
  • valuetype 是键对应的值范例。
   在声明的时间不必要知道 map 的长度,由于 map 是可以动态增长的,未初始化的 map 的值是 nil,使用函数 len() 可以获取 map 中 键值对的数量。
  1. package main
  2. import "fmt"
  3. func main() {
  4.     var mapLit map[string]int
  5.     var mapAssigned map[string]int
  6.     mapLit = map[string]int{"one": 1, "two": 2}
  7.     mapAssigned = mapLit
  8.     //mapAssigned 是 mapList 的引用,对 mapAssigned 的修改也会影响到 mapList 的值。
  9.     mapAssigned["two"] = 3
  10.     fmt.Printf("Map literal at "one" is: %d\n", mapLit["one"])
  11.     fmt.Printf("Map assigned at "two" is: %d\n", mapLit["two"])
  12.     fmt.Printf("Map literal at "ten" is: %d\n", mapLit["ten"])
  13. }
复制代码
map的别的一种创建方式:
  1. make(map[keytype]valuetype)
复制代码
牢记不要使用new创建map,否则会得到一个空引用的指针
map 可以根据新增的 key-value 动态的伸缩,因此它不存在固定长度大概最大限定,但是也可以选择标明 map 的初始容量 capacity,格式如下:
  1. make(map[keytype]valuetype, cap)
复制代码
比方:
  1. map2 := make(map[string]int, 100)
复制代码
当 map 增长到容量上限的时间,如果再增长新的 key-value,map 的巨细会自动加 1,以是出于性能的思量,对于大的 map 大概会快速扩张的 map,纵然只是大概知道容量,也最好先标明。
既然一个 key 只能对应一个 value,而 value 又是一个原始范例,那么如果一个 key 要对应多个值怎么办?
答案是:使用切片
比方,当我们要处置处罚 unix 呆板上的全部历程,以父历程(pid 为整形)作为 key,全部的子历程(以全部子历程的 pid 构成的切片)作为 value。
通过将 value 界说为 []int 范例大概其他范例的切片,就可以优雅的办理这个题目,示例代码如下所示:
  1. mp1 := make(map[int][]int)
  2. mp2 := make(map[int]*[]int)
复制代码
遍历map

map 的遍历过程使用 for range 循环完成,代码如下:
  1. scene := make(map[string]int)
  2. scene["cat"] = 66
  3. scene["dog"] = 4
  4. scene["pig"] = 960
  5. for k, v := range scene {
  6.     fmt.Println(k, v)
  7. }
复制代码
注意:map是无序的,不要盼望 map 在遍历时返回某种盼望次序的效果
删除

使用 delete() 内建函数从 map 中删除一组键值对,delete() 函数的格式如下:
  1. delete(map, 键)
复制代码
map 为要删除的 map 实例,键为要删除的 map 中键值对的键。
  1. scene := make(map[string]int)
  2. // 准备map数据
  3. scene["cat"] = 66
  4. scene["dog"] = 4
  5. scene["pig"] = 960
  6. delete(scene, "dog")
  7. for k, v := range scene {
  8.     fmt.Println(k, v)
  9. }
复制代码
Go语言中并没有为 map 提供任何清空全部元素的函数、方法,清空 map 的唯一办法就是重新 make 一个新的 map,不消担心垃圾接纳的服从,Go语言中的并行垃圾接纳服从比写一个清空函数要高效的多。
注意map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。
线程安全的map

并发情况下读写 map 时会出现题目,代码如下:
  1. // 创建一个int到int的映射
  2. m := make(map[int]int)
  3. // 开启一段并发代码
  4. go func() {
  5.     // 不停地对map进行写入
  6.     for {
  7.         m[1] = 1
  8.     }
  9. }()
  10. // 开启一段并发代码
  11. go func() {
  12.     // 不停地对map进行读取
  13.     for {
  14.         _ = m[1]
  15.     }
  16. }()
  17. // 无限循环, 让并发程序在后台执行
  18. for {
  19. }
复制代码
运行代码会报错,输出如下:
fatal error: concurrent map read and map write
错误信息表现,并发的 map 读和 map 写,也就是说使用了两个并发函数不停地对 map 举行读和写而发生了竞态题目,map 内部会对这种并发利用举行查抄并提前发现。
必要并发读写时,一样平常的做法是加锁,但如许性能并不高,Go语言在 1.9 版本中提供了一种服从较高的并发安全的 sync.Map,sync.Map 和 map 差别,不是以语言原生形态提供,而是在 sync 包下的特殊布局。
sync.Map 有以下特性:


  • 无须初始化,直接声明即可。
  • sync.Map 不能使用 map 的方式举行取值和设置等利用,而是使用 sync.Map 的方法举行调用,Store 表现存储,Load 表现获取,Delete 表现删除。
  • 使用 Range 共同一个回调函数举行遍历利用,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在必要继续迭代遍历时,返回 true,停止迭代遍历时,返回 false。
  1. package main
  2. import (
  3.       "fmt"
  4.       "sync"
  5. )
  6. func main() {
  7.     //sync.Map 不能使用 make 创建
  8.     var scene sync.Map
  9.     // 将键值对保存到sync.Map
  10.     //sync.Map 将键和值以 interface{} 类型进行保存。
  11.     scene.Store("greece", 97)
  12.     scene.Store("london", 100)
  13.     scene.Store("egypt", 200)
  14.     // 从sync.Map中根据键取值
  15.     fmt.Println(scene.Load("london"))
  16.     // 根据键删除对应的键值对
  17.     scene.Delete("london")
  18.     // 遍历所有sync.Map中的键值对
  19.     //遍历需要提供一个匿名函数,参数为 k、v,类型为 interface{},每次 Range() 在遍历一个元素时,都会调用这个匿名函数把结果返回。
  20.     scene.Range(func(k, v interface{}) bool {
  21.         fmt.Println("iterate:", k, v)
  22.         return true
  23.     })
  24. }
复制代码
sync.Map 为了包管并发安全有一些性能丧失,因此在非并发情况下,使用 map 相比使用 sync.Map 会有更好的性能。
常见关键字

nil

在Go语言中,布尔范例的零值(初始值)为 false,数值范例的零值为 0,字符串范例的零值为空字符串"",而指针、切片、映射、通道、函数和接口的零值则是 nil。
nil和其他语言的null是差别的。
nil 标识符是不能比力的
  1. package main
  2. import (
  3.     "fmt"
  4. )
  5. func main() {
  6.     //invalid operation: nil == nil (operator == not defined on nil)
  7.     fmt.Println(nil==nil)
  8. }
复制代码
nil 不是关键字或保存字
nil 并不是Go语言的关键字大概保存字,也就是说我们可以界说一个名称为 nil 的变量,好比下面如许:
  1. //但不提倡这样做
  2. var nil = errors.New("my god")
复制代码
nil 没有默认范例
  1. package main
  2. import (
  3.     "fmt"
  4. )
  5. func main() {
  6.     //error :use of untyped nil
  7.     fmt.Printf("%T", nil)
  8.     print(nil)
  9. }
复制代码
差别范例 nil 的指针是一样的
  1. package main
  2. import (
  3.     "fmt"
  4. )
  5. func main() {
  6.     var arr []int
  7.     var num *int
  8.     fmt.Printf("%p\n", arr)
  9.     fmt.Printf("%p", num)
  10. }
复制代码
nil 是 map、slice、pointer、channel、func、interface 的零值
  1. package main
  2. import (
  3.     "fmt"
  4. )
  5. func main() {
  6.     var m map[int]string
  7.     var ptr *int
  8.     var c chan int
  9.     var sl []int
  10.     var f func()
  11.     var i interface{}
  12.     fmt.Printf("%#v\n", m)
  13.     fmt.Printf("%#v\n", ptr)
  14.     fmt.Printf("%#v\n", c)
  15.     fmt.Printf("%#v\n", sl)
  16.     fmt.Printf("%#v\n", f)
  17.     fmt.Printf("%#v\n", i)
  18. }
复制代码
零值是Go语言中变量在声明之后但是未初始化被赋予的该范例的一个默认值。
差别范例的 nil 值占用的内存巨细大概是不一样的
  1. package main
  2. import (
  3.     "fmt"
  4.     "unsafe"
  5. )
  6. func main() {
  7.     var p *struct{}
  8.     fmt.Println( unsafe.Sizeof( p ) ) // 8
  9.     var s []int
  10.     fmt.Println( unsafe.Sizeof( s ) ) // 24
  11.     var m map[int]bool
  12.     fmt.Println( unsafe.Sizeof( m ) ) // 8
  13.     var c chan string
  14.     fmt.Println( unsafe.Sizeof( c ) ) // 8
  15.     var f func()
  16.     fmt.Println( unsafe.Sizeof( f ) ) // 8
  17.     var i interface{}
  18.     fmt.Println( unsafe.Sizeof( i ) ) // 16
  19. }
复制代码
详细的巨细取决于编译器和架构
new和make



  • new 实用于值范例(int、struct、array),仅分配内存,不初始化。
  • make 用于引用范例(slice、map、channel),分配内存并初始化。
  • interface 和 func 不能用 new,只能直接赋值详细实现。
make 关键字的紧张作用是创建 slice、map 和 Channel 等内置的数据布局,而 new 的紧张作用是为范例申请一片内存空间,并返回指向这片内存的指针。

  • make 分配空间后,会举行初始化,new分配的空间被清零
  • *new 分配返回的是指针,即范例 Type。make 返回引用,即 Type;
if else

在Go语言中,关键字if是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件创建,则会实验 if 后由大括号{}括起来的代码块,否则就忽略该代码块继续实验后续的代码。
  1. if condition {
  2.     // 条件为真执行
  3. }
复制代码
condition 称之为条件表达式大概布尔表达式,实验效果需返回true或false。{ 必须在条件表达式的尾部
  1. x := 0
  2. if x <= 0 {
  3.     fmt.Println("为真进入这里")
  4. }
复制代码
如果存在第二个分支,则可以在上面代码的底子上添加 else 关键字以及另一代码块,这个代码块中的代码只有在条件不满足时才会实验,if 和 else 后的两个代码块是相互独立的分支,只能实验此中一个。
  1. if condition {
  2.     // 条件为真 执行
  3. } else {
  4.     // 条件不满足 执行
  5. }
复制代码
  1.         x := 5
  2.         if x <= 0 {
  3.                 fmt.Println("为真进入这里")
  4.         //go语言格式要求很严,else必须写在}后面
  5.     }else{
  6.         fmt.Println("为假进入这里")
  7.     }
复制代码
如果存在第三个分支,则可以使用下面这种三个独立分支的情势:
  1. if condition1 {
  2.     // condition1 满足 执行
  3. } else if condition2 {
  4.     // condition1 不满足 condition2满足 执行
  5. }else {
  6.     // condition1和condition2都不满足 执行
  7. }
复制代码
  else if 分支的数量是没有限定的,但是为了代码的可读性,照旧不要在 if 反面参加太多的 else if 布局
  if语句可以嵌套:
  1. /* 定义局部变量 */
  2.    var a int = 100
  3.    var b int = 200
  4.    /* 判断条件 */
  5.    if a == 100 {
  6.        /* if 条件语句为 true 执行 */
  7.        if b == 200 {
  8.           /* if 条件语句为 true 执行 */
  9.           fmt.Printf("a 的值为 100 , b 的值为 200\n" )
  10.        }
  11.    }
复制代码
特殊写法

if 另有一种特殊的写法,可以在 if 表达式之前添加一个实验语句,再根据变量值举行判定,代码如下:
  1. if a := 10; a >5 {
  2.     fmt.Println(a)
  3.     return
  4. }
复制代码
这种写法可以将返回值与判定放在一行举行处置处罚,而且返回值的作用范围被限定在 if、else 语句组合中。
   在编程中,变量的作用范围越小,所造成的题目大概性越小,每一个变量代表一个状态,有状态的地方,状态就会被修改,函数的局部变量只会影响一个函数的实验,但全局变量大概会影响全部代码的实验状态,因此限定变量的作用范围对代码的稳固性有很大的资助。
  for

   go语言中的循环语句只支持 for 关键字,这个其他语言是差别的。
  1. sum := 0
  2. //i := 0; 赋初值,i<10 循环条件 如果为真就继续执行 ;i++ 后置执行 执行后继续循环
  3. for i := 0; i < 10; i++ {
  4.     sum += i
  5. }
复制代码
第二种写法:
  1. sum := 0
  2. for {
  3.     sum++
  4.     if sum > 100 {
  5.         //break是跳出循环
  6.         break
  7.     }
  8. }
复制代码
上述的代码,如果没有break跳出循环,那么其将无穷循环
第三种写法:
  1. n := 10
  2. for n>0 {
  3.     n--
  4.     fmt.Println(n)
  5. }
复制代码
我们来看下面一种写法:
  1. step := 2
  2. //初值可以省略,但是;必须有,但是这样写step的作用域就比较大了,脱离了for循环
  3. for ; step > 0; step-- {
  4.     fmt.Println(step)
  5. }
复制代码
进一步简化代码,将 if 判定整合到 for 中,变为下面的代码:
  1. step := 2
  2. for step > 0 {
  3.     step--
  4.     fmt.Println(step)
  5. }
复制代码
竣事循环的方式:

  • return
    1. step := 2
    2. for step > 0 {
    3.     step--
    4.     fmt.Println(step)
    5.     //执行一次就结束了
    6.     return
    7. }
    8. //不会执行
    9. fmt.Println("结束之后的语句....")
    复制代码
  • break
    1. step := 2
    2. for step > 0 {
    3.     step--
    4.     fmt.Println(step)
    5.     //跳出循环,还会继续执行循环外的语句
    6.     break
    7. }
    8. //会执行
    9. fmt.Println("结束之后的语句....")
    复制代码
  • painc
    1. step := 2
    2. for step > 0 {
    3.                 step--
    4.                 fmt.Println(step)
    5.                 //报错了,直接结束
    6.                 panic("出错了")
    7.         }
    8.         //不会执行
    9.         fmt.Println("结束之后的语句....")               
    复制代码
  • goto
    1. package main
    2. import "fmt"
    3. func main() {
    4.     for x := 0; x < 10; x++ {
    5.         for y := 0; y < 10; y++ {
    6.             if y == 2 {
    7.                 // 跳转到标签
    8.                 goto breakHere
    9.             }
    10.         }
    11.     }
    12.     // 手动返回, 避免执行进入标签
    13.     return
    14.     // 标签
    15. breakHere:
    16.     fmt.Println("done")
    17. }
    复制代码
代码优化

  1. package main
  2. func length(s string) int {
  3.         println("call length.")
  4.         return len(s)
  5. }
  6. func main() {
  7.         s := "abcd"
  8.     // 这样写会多次调用length函数
  9.         for i:= 0; i < length(s); i++ {     
  10.                 println(i, s[i])
  11.         }
  12. }
复制代码
优化:
  1. package main
  2. func length(s string) int {
  3.         println("call length.")
  4.         return len(s)
  5. }
  6. func main() {
  7.         s := "abcd"
  8.     // 这样写会多次调佣length函数
  9.         for i,n:= 0,length(s); i <n; i++ {     
  10.                 println(i, s[i])
  11.         }
  12. }
复制代码
案例

   输出九九乘法表
  1. package main
  2. import "fmt"
  3. func main() {
  4.     // 遍历, 决定处理第几行
  5.     for y := 1; y <= 9; y++ {
  6.         // 遍历, 决定这一行有多少列
  7.         for x := 1; x <= y; x++ {
  8.             fmt.Printf("%d*%d=%d ", x, y, x*y)
  9.         }
  10.         // 手动生成回车
  11.         fmt.Println()
  12.     }
  13. }
复制代码
for range

   for range 布局是Go语言特有的一种的迭代布局,for range 可以遍历数组、切片、字符串、map 及管道(channel)
  1. for key, val := range coll {
  2.     ...
  3. }
复制代码
val 始终为集会集对应索引的值拷贝,因此它一样平常只具有只读性子,对它所做的任何修改都不会影响到集会集原有的值
遍历map:
  1. m := map[string]int{
  2.     "hello": 100,
  3.     "world": 200,
  4. }
  5. for key, value := range m {
  6.     fmt.Println(key, value)
  7. }
复制代码
字符串也可以使用for range:
  1. str := "go教程"
  2. //因为一个字符串是 Unicode 编码的字符(或称之为 rune )集合
  3. //char 实际类型是 rune 类型
  4. for pos, char := range str {
  5.     fmt.Println(pos,char)
  6. }
复制代码
每个 rune 字符和索引在 for range 循环中是逐一对应的,它可以大概自动根据 UTF-8 规则辨认 Unicode 编码的字符。
通过 for range 遍历的返回值有肯定的规律:


  • 数组、切片、字符串返回索引和值。
  • map 返回键和值。
  • channel只返回管道内的值。
switch

   switch 语句用于基于差别条件实验差别动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
  switch 分支表达式可以是恣意范例,不限于常量。可省略 break,默认自动停止。
  switch 语句的语法如下:
  1. switch var1 {
  2.     case val1:
  3.         ...
  4.     case val2:
  5.         ...
  6.     default:
  7.         ...
  8. }
复制代码
变量 var1 可以是任何范例,而 val1 和 val2 则可以是同范例的恣意值。
范例不被范围于常量或整数,但必须是雷同的范例;大概终极效果为雷同范例的表达式。
您可以同时测试多个大概符合条件的值,使用逗号分割它们,比方:case val1, val2, val3。
  1. /* 定义局部变量 */
  2.         var grade string = "B"
  3.         var score int = 90
  4.         switch score {
  5.                 case 90: grade = "A"
  6.                 case 80: grade = "B"
  7.                 case 50,60,70 : grade = "C"
  8.                 default: grade = "D"
  9.         }
  10.         //swtich后面如果没有条件表达式,则会对true进行匹配
  11.         //swtich后面如果没有条件表达式,则会对true进行匹配
  12.         switch {
  13.                 case grade == "A" :
  14.                         fmt.Printf("优秀!\n" )
  15.                 case grade == "B", grade == "C" :
  16.                         fmt.Printf("良好\n" )
  17.                 case grade == "D" :
  18.                         fmt.Printf("及格\n" )
  19.                 case grade == "F":
  20.                         fmt.Printf("不及格\n" )
  21.                 default:
  22.                         fmt.Printf("差\n" )
  23.         }
  24.         fmt.Printf("你的等级是 %s\n", grade )
复制代码
  Go内里switch默认相称于每个case末了带有break,匹配乐成后不会自动向下实验其他case,而是跳出整个switch, 那么怎样做到实验完一个case之后,进入下一个case而不是跳出swtich呢?
  答案是:fallthrough
  1. var s = "hello"
  2. switch {
  3. case s == "hello":
  4.     fmt.Println("hello")
  5.     fallthrough
  6. case s != "world":
  7.     fmt.Println("world")
  8. }
复制代码
注意事项:

  • 加了fallthrough后,会直接运行【紧跟的后一个】case或default语句,岂论条件是否满足都会实验
    1. var s = "hello"
    2. switch {
    3. case s == "hello":
    4.     fmt.Println("hello")
    5.     fallthrough
    6. case s == "world":
    7.     fmt.Println("world")
    8. }
    复制代码
goto

   goto 语句通过标签举行代码间的无条件跳转,同时 goto 语句在快速跳出循环、克制重复退出上也有肯定的资助,使用 goto 语句能简化一些代码的实现过程。
  使用 goto 退出多层循环
传统写法:
  1. package main
  2. import "fmt"
  3. func main() {
  4.     var breakAgain bool
  5.     // 外循环
  6.     for x := 0; x < 10; x++ {
  7.         // 内循环
  8.         for y := 0; y < 10; y++ {
  9.             // 满足某个条件时, 退出循环
  10.             if y == 2 {
  11.                 // 设置退出标记
  12.                 breakAgain = true
  13.                 // 退出本次循环
  14.                 break
  15.             }
  16.         }
  17.         // 根据标记, 还需要退出一次循环
  18.         if breakAgain {
  19.                 break
  20.         }
  21.     }
  22.     fmt.Println("done")
  23. }
复制代码
使用goto的写法:
  1. package main
  2. import "fmt"
  3. func main() {
  4.     for x := 0; x < 10; x++ {
  5.         for y := 0; y < 10; y++ {
  6.             if y == 2 {
  7.                 // 跳转到标签
  8.                 goto breakHere
  9.             }
  10.         }
  11.     }
  12.     // 手动返回, 避免执行进入标签
  13.     return
  14.     // 标签
  15. breakHere:
  16.     fmt.Println("done")
  17. }
复制代码
使用 goto 语句后,无须额外的变量就可以快速退出全部的循环
使用 goto 会集处置处罚错误
多处错误处置处罚 存在代码重复 比方:
  1. package main
  2. import (
  3.         "errors"
  4.         "fmt"
  5.         "os"
  6. )
  7. func main() {
  8.         err := firstCheckError()
  9.         if err != nil {
  10.                 fmt.Println(err)
  11.                 exitProcess()
  12.         }
  13.         err = secondCheckError()
  14.         if err != nil {
  15.                 fmt.Println(err)
  16.                 exitProcess()
  17.         }
  18.         fmt.Println("done")
  19. }
  20. func secondCheckError() interface{} {
  21.         return errors.New("错误2")
  22. }
  23. func exitProcess() {
  24.         //退出
  25.         os.Exit(1)
  26. }
  27. func firstCheckError() interface{} {
  28.         return errors.New("错误1")
  29. }
复制代码
使用goto:
  1. package main
  2. import (
  3.         "errors"
  4.         "fmt"
  5.         "os"
  6. )
  7. func main() {
  8.         err := firstCheckError()
  9.         if err != nil {
  10.                 fmt.Println(err)
  11.                 goto onExit
  12.         }
  13.         err = secondCheckError()
  14.         if err != nil {
  15.                 fmt.Println(err)
  16.                 goto onExit
  17.         }
  18.         fmt.Println("done")
  19.         return
  20.         onExit:
  21.                 exitProcess()
  22. }
  23. func secondCheckError() interface{} {
  24.         return errors.New("错误2")
  25. }
  26. func exitProcess() {
  27.         fmt.Println("exit")
  28.         //退出
  29.         os.Exit(1)
  30. }
  31. func firstCheckError() interface{} {
  32.         return errors.New("错误1")
  33. }
复制代码
break

   break 语句可以竣事 for、switch 和 select 的代码块,别的 break 语句还可以在语句反面添加标签,表现退出某个标签对应的代码块,标签要求必须界说在对应的 for、switch 和 select 的代码块上。
  1. package main
  2. import "fmt"
  3. func main() {
  4. OuterLoop:
  5.     for i := 0; i < 2; i++ {
  6.         for j := 0; j < 5; j++ {
  7.             switch j {
  8.             case 2:
  9.                 fmt.Println(i, j)
  10.                 break OuterLoop
  11.             case 3:
  12.                 fmt.Println(i, j)
  13.                 break OuterLoop
  14.             }
  15.         }
  16.     }
  17. }
复制代码
continue

   continue 语句可以竣事当前循环,开始下一次的循环迭代过程,仅限在 for 循环内使用,在 continue 语句后添加标签时,表现开始标签对应的循环
  1. package main
  2. import "fmt"
  3. func main() {
  4. OuterLoop:
  5.     for i := 0; i < 2; i++ {
  6.         for j := 0; j < 5; j++ {
  7.             switch j {
  8.             case 2:
  9.                 fmt.Println(i, j)
  10.                 continue OuterLoop
  11.             }
  12.         }
  13.     }
  14. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表