Go语言代码块与作用域理解

这篇具有很好参考价值的文章主要介绍了Go语言代码块与作用域理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

如果不深入理解 Go 语言的代码块作用域,程序将产生我们无法理解的行为,比如说在循环中创建 goroutine func, 为什么需要传递参数至 goroutine 内部,否则所有的 func 使用的变量参数都是循环的最后一个值。
看下边这个 demo, 就需要深入理解 Go 语言代码块的作用域才能理直气壮的一口答对:

func main() {
    if a := 1; false {
    } else if b := 2; false {
    } else if c := 3; false {
    } else {
        println(a, b, c)
    }
}

有两个答案选项:
A:1 2 3
B:无法通过编译
思考三秒钟🤔-----------------
正确答案是 A,你答对了吗,接下来将围绕上述例子来理解一下Go代码块(code block)和作用域(scope)规则,理解这些规则将有助于我们编写出正确且可读性高的代码。

代码块与作用域简介

Go语言中的代码块是包裹在一对大括号内部的声明和语句,且代码块支持嵌套。如果一对大括号之间没有任何语句,那么称这个代码块为空代码块。代码块是代码执行流流转的基本单元,代码执行流总是从一个代码块跳到另一个代码块。
显示代码块

  • 由大括号包裹,比如函数体、for 循环的循环体、if 语句的某个分支等。
    image.png

隐式代码块

  • 宇宙代码块: 所有Go源码都在该隐式代码块中,就相当于所有Go代码的最外层都存在一对大括号。
  • 包代码块: 每个包都有一个包代码块,其中放置着该包的所有Go源码。
  • 文件代码块:每个文件都有一个文件代码块,其中包含着该文件中的所有Go源码。
  • 每个if、for和switch语句均被视为位于其自己的隐式代码块中。
  • switch或select语句中的每个子句都被视为一个隐式代码块。

Go标识符的作用域是基于代码块定义的,作用域规则描述了标识符在哪些代码块中是有效的。下面是标识符作用域规则。
在函数内部声明的类型标识符的作用域范围始于类型定义中的标识符,止于其最里面的那个包含块的末尾:
image.png

if 条件控制语句的代码块

  1. 单 if 型

    同时包含一个隐式代码块和一个显式代码块:
    image.png

    func Foo() {
        if a := 1; true {
            fmt.Println(a)
        }
    }
    
    // 等价变换为
    func Foo() {
        {
            a := 1
            if true {
            	fmt.Println(a)
        	}
        }
    }
    
  2. if {} else {} 型

    包含三个代码块,单 if 的两个代码块,else 的显式代码块:
    image.png

    func Foo() {
        if a,b := 1, 2; false {
            fmt.Println(a)
        } else {
            fmt.Println(b)
        }
    }
    
    // 等价变换为
    func Foo() {
        {
            a, b := 1, 2
            if false {
                fmt.Println(a)
            } else {
            	fmt.Println(b)
        	}
        }
    }
    
  3. if {} else if {} else {} 型image.png**对上边的伪代码进行变换:
    image.png
    此时就可以对一开始的答案进行解释了:

    func main() {
        if a := 1; false {
        } else if b := 2; false {
        } else if c := 3; false {
        } else {
            println(a, b, c)
        }
    }
    
    // 进行等价变换后
    func main() {
        {
            a := 1
            if false {
            } else {
                {
                    b := 2
                    if false {
                    } else {
                        {
                            c := 3
                            if false {
                            } else {
                                println(a, b, c)
                            }
                        }
                    }
                }
            }
        }
    }
    

其他控制语句的代码块

  1. for 语句的代码块
    通用 for 控制语句等价变换:
    image.png

    for a, b := 1, 10; a < b; a++ {
        ...
    }
    
    // 等价转换为
    {
       	a, b := 1, 10
        for ; a < b; a++ {
        	...
    	}
    }
    

    for range 语句等价变换:
    image.png

    var sl = []int{1, 2, 3}
    for i, n := range sl {
        ...
    }
    
    // 等价变换为
    var sl = []int{1, 2, 3}
    {
        i, n := 0, 0
        for i, n := range sl {
        	...
    	}
    }
    
  2. switch-case 语句的代码块
    通用形式等价变换:
    image.png

    switch x, y := 1, 2; x + y {
    case 3:
        a := 1
        fmt.Println("case1: a = ", a)
        fallthrough
    case 10:
        a := 5
        fmt.Println("case2: a = ", a)
        fallthrough
    default:
        a := 7
        fmt.Println("default case: a = ", a)
    }
    
    
    // 等价变换为
    {
        x, y := 1, 2
        switch x + y {
        case 3:
            {
                a := 1
                fmt.Println("case1: a = ", a)
            }
            fallthrough
        case 10:
            {
                a := 5
                fmt.Println("case2: a = ", a)
            }
            fallthrough
        default:
            {
                a := 7
                fmt.Println("default case: a = ", a)
            }
        }
    }
    
  3. select-case 语句的代码块

    通用形式等价变换:
    image.png

    c1 := make(chan int)
    c2 := make(chan int, 1)
    c2 <- 11
    select {
    case c1 <- 1:
        fmt.Println("SendStmt case has been chosen")
    case i := <-c2:
        _ = i
        fmt.Println("RecvStmt case has been chosen")
    default:
        fmt.Println("default case has been chosen")
    }
    
    // 等价变换为 (伪代码)
    c1 := make(chan int)
    c2 := make(chan int, 1)
    c2 <- 11
    select {
    case c1 <- 1:
        {
            fmt.Println("SendStmt case has been chosen")
        }
    case "如果该case 被选择":
        {
            i := <-c2:
            _ = i
            fmt.Println("RecvStmt case has been chosen")
        }
    default:
        {
            fmt.Println("default case has been chosen")
        }
    }
    

总结

各类隐式代码块的规则才是理解Go代码块和作用域的规则的“金钥匙”,尤其是那些对于程序执行流有重大影响的控制语句的隐式代码块规则。
理解Go代码块和作用域的规则将有助于我们快速解决类似“变量未定义”的错误和上一层变量被内层同名变量遮蔽(shadow)的问题,同时对于正确理解Go程序的执行流也大有裨益。

Go语言代码块与作用域理解文章来源地址https://www.toymoban.com/news/detail-510133.html

到了这里,关于Go语言代码块与作用域理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入理解GO语言——GC垃圾回收二

    书接上回,无论怎么优化,Go V1.3都面临这个一个重要问题,就是mark-and-sweep 算法会暂停整个程序 。 Go是如何面对并这个问题的呢?接下来G V1.5版本 就用 三色并发标记法 来优化这个问题 Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一

    2024年04月11日
    浏览(36)
  • Go基础12-理解Go语言表达式的求值顺序

    Go语言在变量声明、初始化以及赋值语句上相比其先祖C语言做了一些改进,诸如: ● 支持在同一行声明和初始化多个变量(不同类型也可以) ● 支持在同一行对多个变量进行赋值 这种语法糖在给我们带来便利的同时,也可能带来一些令人困惑的问题。 Go语言之父Rob Pike在

    2024年02月09日
    浏览(57)
  • 初始Go语言2【标识符与关键字,操作符与表达式,变量、常量、字面量,变量作用域,注释与godoc】

      go变量、常量、自定义类型、包、函数的命名方式必须遵循以下规则: 首字符可以是任意Unicode字符或下划线。 首字符之外的部分可以是Unicode字符、下划线或数字。 名字的长度无限制。 理论上名字里可以有汉字,甚至可以全是汉字,但实际中不要这么做。 Go语言

    2023年04月09日
    浏览(52)
  • Go语言代码断行规则详解

    本文深入探讨了Go语言中代码断行的各个方面,从基础概念到实际应用实践。 关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架

    2024年02月08日
    浏览(47)
  • 掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)

    在Go语言中,范围(range)用于迭代数组、切片、映射、通道等数据结构的元素。范围的语法形式为 for range ,可以遍历集合中的每个元素,并在每次迭代中返回元素的索引(或键)和对应的值。 Go语言范围使用方法 使用范围语句的一般形式为: 其中, index 是元素的索引(或

    2024年04月17日
    浏览(45)
  • Go语言——【高质量编程 | 代码规范】

    作者 :非妃是公主 专栏 :《Golang》 博客主页 :https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 高质量编程,就是指编写的代码能够达到正确可靠的同时,具备简洁清晰、结构明了等特点,可以让团队成员很快的上手。具体来说,有

    2024年02月01日
    浏览(92)
  • 【Go 基础篇】Go语言包详解:模块化开发与代码复用

    在Go语言中, 包(Package) 是一种用于组织代码的机制,用于将相关的函数、类型和变量等组织在一起,以便于模块化开发和代码复用。包的使用能够使程序结构更加清晰、可维护性更高,同时也是Go语言强调的一项重要特性。本篇博客将深入探讨Go语言中包的相关知识,包括

    2024年02月11日
    浏览(45)
  • Go语言用Resty库编写的音频爬虫代码

    目录 一、Go语言与Resty库简介 二、音频爬虫的实现 1、确定抓取目标 2、使用Resty发送HTTP请求 3、解析响应数据 4、下载音频文件 5、并发下载音频文件 三、注意事项 总结 随着互联网的飞速发展,网络爬虫逐渐成为数据获取和分析的重要工具。在音频领域,通过爬虫技术,我们

    2024年02月08日
    浏览(39)
  • 36-代码测试(上):如何编写Go语言单元测试和性能测试用例?

    每种语言通常都有自己的测试包/模块,Go语言也不例外。在Go中,我们可以通过 testing 包对代码进行单元测试和性能测试。  Go语言有自带的测试框架 testing ,可以用来实现单元测试(T类型)和性能测试(B类型),通过 go test 命令来执行单元测试和性能测试。 go test 执行测试

    2024年04月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包