Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24139 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

18 | if语句、for语句和switch语句

郝林 2018-09-21
在上两篇文章中,我主要为你讲解了与go语句、goroutine 和 Go 语言调度器有关的知识和技法。
内容很多,你不用急于完全消化,可以在编程实践过程中逐步理解和感悟,争取夯实它们。
现在,让我们暂时走下神坛,回归民间。我今天要讲的if语句、for语句和switch语句都属于 Go 语言的基本流程控制语句。它们的语法看起来很朴素,但实际上也会有一些使用技巧和注意事项。我在本篇文章中会以一系列面试题为线索,为你讲述它们的用法。
那么,今天的问题是:使用携带range子句的for语句时需要注意哪些细节? 这是一个比较笼统的问题。我还是通过编程题来讲解吧。
本问题中的代码都被放在了命令源码文件 demo41.go 的main函数中的。为了专注问题本身,本篇文章中展示的编程题会省略掉一部分代码包声明语句、代码包导入语句和main函数本身的声明部分。
numbers1 := []int{1, 2, 3, 4, 5, 6}
for i := range numbers1 {
if i == 3 {
numbers1[i] |= i
}
}
fmt.Println(numbers1)
我先声明了一个元素类型为int的切片类型的变量numbers1,在该切片中有 6 个元素值,分别是从16的整数。我用一条携带range子句的for语句去迭代numbers1变量中的所有元素值。
在这条for语句中,只有一个迭代变量i。我在每次迭代时,都会先去判断i的值是否等于3,如果结果为true,那么就让numbers1的第i个元素值与i本身做按位或的操作,再把操作结果作为numbers1的新的第i个元素值。最后我会打印出numbers1的值。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(19)

  • 咖啡色的羊驼
    好久没留言了,

    1.断言判断value.(type)
    2.if的判断的域和后面跟着的花括号里头的域。和函数雷同,参数和花括号里头的域同一个

    作者回复: 好,继续加油吧。

    2018-09-21
    12
  • 澎湃哥
    好像还没有人回答数组变切片的问题,贴一下运行结果吧:
    i:0, e:1
    i:1, e:3
    i:2, e:6
    i:3, e:10
    i:4, e:15
    i:5, e:21
    [22 3 6 10 15 21]

    每次循环打印了一个索引和值,看起来 range 切片的话,是会每次取 slice[i] 的值,但是应该还是发生了拷贝,不能通过 e 直接修改原值。
    2018-11-29
    5
  • Zzz
    个人理解: for .. range .. 实际上可以认为是方法调用的语法糖,range后面的变量就是方法参数,对于数组类型的变量,传入的参数是数组的副本,更新的是原数组的元素,取的是副本数组的元素;对于切片类型的变量,传入的参数是切片的副本,但是它指向的底层数组与原切片相同,所以取的元素和更新的元素都是同一个数组的元素。
    2019-05-15
    3
  • Leon📷
    val.(type)需要提前将类型转换成interface{},一楼的留言有点问题
    2018-10-22
    3
  • 江山如画
    第一个问题,在类型switch语句中,如何对被判断类型的那个值做类型转换,尝试在 switch 语句中重新定义了一个 uint8 类型的变量和被判断类型的值做加法操作,一共尝试了三种方法,发现需要使用 type assertion 才可以,强转或者直接相加都会出错。

    转换语句是:val.(uint8)

    完整验证代码:

    val := interface{}(byte(1))
    switch t := val.(type) {
    case uint8:
    var c uint8 = 2

    //use type assertion
    fmt.Println(c + val.(uint8))

    //invalid operation: c + val (mismatched types uint8 and interface {})
    //fmt.Println(c + val)

    //cannot convert val (type interface {}) to type uint8: need type assertion
    //fmt.Println(c + uint8(val))

    default:
    fmt.Printf("unsupported type: %T", t)
    }

    第二个问题,在if语句中,初始化子句声明的变量的作用域是在该if语句之内,if语句之外使用该变量会提示 “undefined”。

    验证代码:

    m := make(map[int]bool)
    if _, ok := m[1]; ok {
    fmt.Printf("exist: %v\n", ok)
    } else {
    fmt.Printf("not exist: %v\n", ok)
    }

    //fmt.Println(ok) //报错,提示 undefined: ok
    2018-10-08
    2
  • 茶底
    老师什么时候讲逃逸分析啊
    2018-09-23
    2
  • hiyanxu
    老师,我想问一下,range的副本,是说k、v是副本,还是被迭代的数组是副本?
    我自己测试在for的里面和外面数组地址是一样的

    作者回复: 迭代变量是副本。另外在Go程序里的变量地址是不能完全说明问题的,因为goroutine的栈空间有可能会被优化。

    2018-12-23
    1
  • 公众号「后端进阶」
    真的很喜欢go的语法与简洁的哲学
    2018-09-21
    1
  • My dream
    Go1.11已经正式发布,最大的一个亮点是增加了对WebAssembly的实验性支持。老师要讲一下不?我们都不懂这个有什么意义

    作者回复: 主要是写Web端的时候有些用,不过我不觉得用处很大,因为现在大型网站都是前后端分离的。最后我视情况而定吧。

    2018-09-21
    1
  • Dr.Li
    感觉go的语法有点变态啊

    作者回复: 要包容:)工具而已。

    2018-09-21
    1
  • Felix
    关于数组变切片那个地方。我理解如下:切片自己不拥有任何数据,它只是底层数组的一种表示,对切片的任何操作都会被反映到底层数组中去。
    package main

    import "fmt"

    func main() {

    numbers3 := []int{1, 2, 3, 4, 5, 6}
    maxIndex3 := len(numbers2) - 1 //6-1= 5
    for i, e := range numbers3 { // 0:1 1:2 2:3 3:4 4:5 5:6
    if i == maxIndex3 { // 5
    numbers3[0] += e // 0,7
    } else {
    numbers3[i+1] += e // 1:3
    }
    // 0:1 1:(1+2)3 2:3 3:4 4:5 5:6
    // 0:1 1:3 2:(3+3)6 3:4 4:5 5:6
    // 0:1 1:3 2:6 3:(6+4)10 4:5 5:6
    // 0:1 1:3 2:6 3:10 4:(10+5)15 5:6
    // 0:1 1:3 2:6 3:10 4:15 5:(15+6)21
    // 0:(21+1)22 1:3 2:6 3:10 4:15 5:21
    // 22 3 6 10 15 21
    }
    fmt.Println(numbers3)
    }

    作者回复: 对,所以我们才称切片为引用类型。它本身只是底层数组及其存储状态的一种描述。

    2019-12-09
  • 大王叫我来巡山
    了解这些只能证明您对这个语言足够的了解,但是实际中谁会写这么蛋疼的代码呢,这一篇通篇其实说明的还是go语言中关于类型转换的内容

    作者回复: 这些语法细节你要是不注意的话,说不定什么时候就会“踩坑”。文章中的代码是为了演示原理而设计的,因此不一定适用于生产。

    你可以去看看哪些热门开源项目的代码,里面仍然会有体现这些知识点的代码。所以不充分理解这些,可能看复杂些的项目源码都费劲。

    2019-09-09
    1
  • 博博
    老师遇到一个问题,希望能帮忙解答下!
    您在文章中说range表达式的结果值是会被复制的,那么是所有的都会被复制么? 我看了资料,发现字典和通道类型好像没有发生复制!

    // Lower a for range over a map.
    // The loop we generate:
    // var hiter map_iteration_struct
    // for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
    // index_temp = *hiter.key
    // value_temp = *hiter.val
    // index = index_temp
    // value = value_temp
    // original body
    // }
    很是疑惑,希望能得到指点!谢谢

    作者回复: 你看的是 Go 的源码吗?我没找到这段代码。不过我看了下 mapiterinit 函数的代码。其中还是有复制的。只不过,对于这些引用类型的值来说,即使有复制也只会复制一些指针而已,底层数据结构是不会被赋值的。

    2019-07-23
  • benying
    学习到一些平时没注意的细节,学习老师
    2019-06-04
  • Geek_1ed70f
    按位或

    有错别字 += 写成了 |=

    作者回复: 这就是按位或啊

    2019-02-26
  • 虢国技匠
    打卡
    2019-01-21
  • jacke
    switch进行有限的转化不是很明白?value1里面switch的语句不能从int转换为int8、那value2里面case语句又可以从int转换为int8?还有借口转换如何判定呢?
    2018-10-24
  • yandongxiao
    咖啡色的羊驼的答案貌似是错误的吧?
    1. 在类型switch语句中,t := value6.(type);匹配到哪个case表达式,t就会是哪种具体的数据类型;
    2. 在if语句中,子句声明的变量的作用域 比 随后的花括号内的变量的的作用域 更大
    if a := 10; a > 0 {
    a := 20
    println(a)
    }
    反证法:如果咖啡色的羊驼说的对,上面的语句不应该编译通过才对。

    关于2这个细微的差异,也适用于for i:=0; i<10; i++ 语句。
    在for语句中的使用匿名函数,很可能出现“loop variable capture”问题。根本原因也是i的作用域与{}中的变量的作用域是不同的。
    2018-09-27
  • hua
    把总结结论放在最前面再看主体内容会容易理解得多。
    2018-09-21
收起评论
19
返回
顶部