Go 语言精进之路——Go语言代码块与作用域理解

news2024/12/30 3:11:15

文章目录

    • 前言
    • 代码块与作用域简介
    • if 条件控制语句的代码块
    • 其他控制语句的代码块

前言

如果不深入理解 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程序的执行流也大有裨益。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/704682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

柠檬精僵尸2023-2024_wb粉丝清道夫比赛博更好用的方法

微博批量移除微博粉丝方法_可视化的界面 多样性选择你要移除清理的粉丝类型 三个按钮就可以搞定了 移除粉丝步骤&#xff1a; 首先打开微博&#xff0c;登陆自己的账号。 找到你想要删除的粉丝&#xff0c;点开他/她的头像&#xff0c;打开他/她的个人主页 如果想要大批量清理…

尚硅谷Docker实战教程-笔记04【Docker镜像】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【理念简介、官网介绍、平台入门图解、平台架构图解】尚硅谷Docker实战教程-笔…

accountId、uid、roleId 游戏中的3种id

1)accountId&#xff1a; // 是一个字符串 这个是字符串&#xff0c;玩家可以输入名字(比如:输入zfoo) 或者 通过微信生成(openid是不变的)。 2)uid&#xff1a;// long 这个是一个很长的,玩家通过http登录授权后&#xff0c;通过web层&#xff0c;由雪花算法根据accountId生成…

【每天40分钟,我们一起用50天刷完 (剑指Offer)】第十一天 11/50

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

Selenium上传文件有多少种方式?不信你有我全

Selenium 封装了现成的文件上传操作。但是随着现代前端框架的发展&#xff0c;文件上传的方式越来越多样。而有一些文件上传的控件&#xff0c;要做自动化控制会更复杂一些&#xff0c;这篇文章主要讨论在复杂情况下&#xff0c;如何通过自动化完成文件上传 1.input 元素上传文…

【FFmpeg实战】AAC编码, 解码

使用命令行进行AAC编码 // PCM的三要素采样率&#xff0c;声道数&#xff0c; 采样格式 ffmpeg -ar 44100 -ac 2 -f s16le -i in.pcm out.aac// -c:a codec:audio 指定的是音频编码 ffmpeg -ar 44100 -ac 2 -f s16le -i in.pcm -c:a libfdk_aac out.aacffmpeg -ar 44100 -ac …

哪些辅助工具支持快递单号的批量查询

当我们批量发出大量快递时&#xff0c;有什么查询软件可以快速查询和跟踪批量每个单号的物流信息&#xff0c;及时跟踪物流状态&#xff1f;今天&#xff0c;我来安利一个软件&#xff1a;“固乔快递查询助手”&#xff0c;可以实现这个功能。我们一起来看看软件的使用和操作步…

C++类与对象(默认成员函数之拷贝构造函数)

接前几次的类与对象的默认函数的知识点&#xff0c;下来面是默认成员函数中的拷贝构造函数。是的&#xff0c;它的名字是拷贝构造函数&#xff0c;他其实也是一种构造函数&#xff0c;为什么呢&#xff1f;接下来你就知道了&#xff0c;我们直接看看代码&#xff0c;如下&#…

【AICG】【Midjourney】AI自动生成图片的初接触

背景 现在是AI 比较流行&#xff0c;公司也推荐学习和了解AI的相关东西。 公司的内部培训中涉及到了Midjourney的简单讲解。 效果 由于我的账号注册的时候过了free窗口期&#xff1a; 现在要我花钱订阅&#xff1a; 只能看看其他人的帖子过过AI生成的瘾。 如图所示&#x…

【TA100】4.2 SSAO算法 屏幕空间环境光遮蔽

1 SSAO介绍 1.1 环境光遮蔽&#xff08;AO&#xff09; 全称Ambient Occlusion,是计算机图形学中一种着色和渲染技术&#xff0c;模拟光线达到物体的能力的粗略的全局方法&#xff0c;描述光线到达物体表面的能力。 这个场景看起来没有表示颜色的贴图&#xff0c;但是场景的…

SC5103接口可pin对pin兼容TLK1501

SC5103 用于超高速双向点对点数据传输系统。SC5103 支持 0.6Gbps 至 1.5Gbps 的有效串行接口速度&#xff0c;提供高达 1.2Gbps 的有效数据带宽。可pin对pin兼容TLK1501。 SC5103 可替代并行传输数据结构从而减少传输路径数、连接端子数、发送/接收端子数。并行数据被发送链路转…

求职贴 | 多源融合定位方向24届硕士秋招求职

求职方向 多源融合定位 / LIO / VIO 基本情况 现就读于华中区某985&#xff0c;研究方向为多源融合定位&#xff0c;主要内容是LiDAR / IMU融合两篇期刊论文在投&#xff0c;论文内容即项目经历&#xff1b;具有丰富的电子设计类竞赛经历自我认为学习能力强&#xff0c;爱动…

JavaSE-15 【异常】

文章目录 JavaSE-15 【异常】第一章 什么是异常1.1 异常的概念1.2 异常的体系1.3 异常的分类1.4 异常产生的过程 第二章 异常的处理2.1 抛出异常throw2.2 声明异常throws2.3 捕获异常try...catch2.4 finally 代码块2.5 异常注意事项 第三章 自定义异常3.1 自定义异常概述3.2 自…

工业物联网解决方案:远程组态监控管理系统

如何高效的采集和集中工业设备PLC运行数据、工艺参数、产品质量等生产数据&#xff0c;通过数据分析反馈在制造工艺、生产流程、质量管理、设备维护和能耗管理的智能应用&#xff0c;这是工业远程组态监控管理系统可以解决的问题&#xff0c;也是众多工厂企业关注的重点。 工…

动态规划--输出路径06.25

https://www.cnblogs.com/jbelial/articles/2116074.html 博客参考 https://www.cnblogs.com/jbelial/articles/2116074.html 12. 背包问题求具体方案 - AcWing题库 由于需要求解最小字典序&#xff0c;尝试输入数据时逆序输入&#xff0c;其他不改变&#xff0c;状态含义不变…

奇安信浏览器调用JM9硬件解码操作教程

本文讲述如何通过奇安信浏览器调用景嘉微JM9系列显卡的硬件解码。 判断硬件解码是否打开方法 1.检查奇安信浏览器版本。dpkg -l | grep qaxbrowser&#xff0c;我这里是1.0.45209.2-1版本。要大于等于这个版本的奇安信浏览器才支持硬件解码。 2.打开奇安信浏览器&#xff…

海思平台OSD的实现

目录 1.海思平台OSD理论学习 1.1、OSD概述 1.2、海思OSD的4种类型 1.3、海思OSD的几个重要概念 1.4、海思平台OSD使用方法 1.5、海思平台OSD的API和关键数据结构 2.使用海思接口实现OSD---代码框架 2.1、函数调用层次 2.2、前\背景透明度、背景颜色 3.使用字库字符实现…

Jmeter远程服务模式运行时引用csv文件的路径配置

目录 前言&#xff1a; 问题 解决方法 前言&#xff1a; 在JMeter远程服务模式下运行时&#xff0c;你可以通过配置CSV文件的路径来引用该文件。CSV文件经常用于存储测试数据&#xff0c;可以在测试中进行参数化。 问题 在使用jmeter过程中&#xff0c;本机的内存等配置不…

掌握GDB调试工具,轻松排除bug!

一、什么是GDB gdb是GNU debugger的缩写&#xff0c;是编程调试工具。 GDB官网&#xff1a; https://www.gnu.org/software/gdb/GDB适用的编程语言&#xff1a; Ada / C / C / objective-c / Pascal 等。GDB的工作方式&#xff1a; 本地调试和远程调试。 目前release的最新版…