07.Go 流程控制

流程控制是Go语言中必不可少的一部分,也是整个编程基础的重要一环。Go语言的流程控制语句和其他编程语言的流程控制语句有些不同,主要体现在Go语言没有do-while语句。Go语言常用的流程控制包括if语句、switch语句、for语句及goto语句等,switch语句和goto语句主要是为了简化代码、降低代码重复率,属于扩展类的流程控制语句。

1、条件判断

在Go语言中,if语句主要用于条件判断。if语句还有两个分支结构:if-else语句和else-if语句

a. if 单分支

在Go语言中,关键字if是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行if后由大括号“{}”括起来的代码块,否则就忽略该代码块继续执行后续的代码。

if 条件表达式 {    代码块
}

使用if语句判断一个变量的大小:

func main() {/*定义局部变量*/var a int = 20if a < 30 {fmt.Printf("a 小于 30\n")}fmt.Printf("a 的值为:%d\n", a)
}

b. if-else 双分支

if语句后可以使用可选的else语句,else语句中的表达式在条件表达式为false时执行。if和else后的两个代码块是相互独立的分支,只能执行其中一个。

if 条件表达式 {    代码块1
} else { 代码块2
}
func main() {/*定义局部变量*/var a int = 50if a < 30 {fmt.Printf("a 小于 30\n")} else {fmt.Printf("a 不小于 30\n")}fmt.Printf("a 的值为:%d\n", a)
}

c. if-else-if 多分支
if 条件表达式 {    代码块1
} else if 条件表达式  { 代码块2
} else {代码块3
}
func main() {/*定义局部变量*/var a int = 15if a < 10 {fmt.Printf("a 小于 10\n")} else if a > 20 {fmt.Printf("a 大于 20\n")} else {fmt.Printf("a 大于 10\n")fmt.Printf("a 小于 20\n")}fmt.Printf("a 的值为:%d\n", a)
}

d. if 语句的注意事项

(1)if后面的条件判断子句不需要使用小括号括起来,例如,if a > 30。

(2)Go语言规定,与if匹配的“{”必须与if和表达式放在同一行,如果尝试将“{”放在其他位置,将会出发编译错误。与else匹配的“{”也必须与else在同一行,else也必须与上一个if或else if的右边的大括号在一行。

(3)if后面可以带一个简单的初始化语句,并以分号进行分隔,该简单语句声明的变量的作用域是整个if语句块,包括后面的else if和else分支。

(4)Go语言没有条件运算符(a>b?a:b),符合Go语言的设计理念,只提供一种方法做事情。

(5)if分支语句如果遇到return,则直接返回

2、选择结构

switch语句和select语句在Go语言中主要用于条件的选择。相比C语言,Go语言中的switch语句在结构上更加灵活,语法设计尽量以使用方便为主。

a. switch 语句

在Go语言中,switch表示选择语句的关键字,switch语句会根据初始化表达式得出一个值,然后根据case语句的条件,执行相应的代码块,最终返回特定内容。每个case被称为一种情况,只有当初始化语句的值符合case的条件语句时,case才会被执行。

如果没有遇到符合的case,则可以使用默认的case (default case),如果已经遇到了符合的case,那么后面的case都不会被执行。

Go语言改进了switch的语法设计,case与case之间是独立的代码块,不需要通过break语句跳出当前case代码块以避免执行到下一行

switch (表达式) {case常数1:代码块1;case常数2:代码块2;……default:代码块n+1;
}
func main() {/*定义局部变量*/a := "hello"switch a {case "hello":fmt.Println("hello")case "world":fmt.Println("world")default:fmt.Println("hello world!!!")}
}

与其他编程语言不同的是,在Go语言编程中,switch有两种类型。

(1)表达式switch:在表达式switch中,case包含与switch表达式的值进行比较的表达式。

(2)类型switch:在类型switch中,case包含与特殊注释的switch表达式的类型进行比较的类型。

表达式switch:

func main() {grade := "E"marks := 95switch {case marks >= 90:grade = "A"case marks >= 80:grade = "B"case marks >= 70:grade = "C"case marks >= 60:grade = "D"default:grade = "E"}switch {case grade == "A":fmt.Println("成绩优秀!")case grade == "B":fmt.Println("表现良好!")case grade == "C", grade == "D":fmt.Println("再接再厉!")default:fmt.Println("成绩不合格!")}fmt.Println("你的成绩为:", grade)
}

类型switch:

类型switch语句针对变量的类型判断该执行哪个case代码块

func main() {x = 1switch i := x.(type) {case nil:fmt.Println("这里是nil,x的类型是%T", i)case int:fmt.Println("这里是int,x的类型是%T", i)case float64:fmt.Println("这里是float64,x的类型是%T", i)case bool:fmt.Println("这里是bool,x的类型是%T", i)case string:fmt.Println("这里是string,x的类型是%T", i)default:fmt.Println("未知类型!")}
}

switch的特点如下:

  • (1)switch和if语句一样,switch后面可以带一个可选的简单的初始化语句。
  • (2)switch后面的表达式也是可选的,如果没有表达式,则case子句是一个布尔表达式,而不是一个值,此时就相当于多重if-else语句。
  • (3)switch条件表达式的值不像C语言那样必须限制为整数,可以是任意支持相等比较运算的类型变量。
  • (4)通过fallthough语句来强制执行下一个case子句(不再判断下一个case子句的条件是否满足)。
  • (5)switch支持default语句,当所有的case分支都不符合时,执行default语句,并且default语句可以放到任意位置,并不影响switch的判断逻辑。
  • (6)switch和.(type)结合可以进行类型的查询。
b. select 语句

在Go语言中,除了switch语句外,还有一种选择结构——select。select语句可以用于配合通道(channel)的读/写操作,用于多个channel的并发读/写操作。

select语句类似于switch语句,switch语句是按照顺序从上到下依次执行,而select是随机选择一个case执行。如果没有case可运行,它将阻塞,直到有case可运行。

select {case:       代码块1;    case:       代码块2;     default : /*可选*/       代码块n;
}
  • 每个case都必须是一个通信。
  • 所有channel表达式都会被求值。
  • 所有被发送的表达式都会被求值。
  • 如果任意某个通信可以进行,它就执行,其他被忽略。
  • 如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。
  • 如果没有可执行的case,但是有default子句,则执行该语句。
  • 如果没有可执行的case,并且也没有default子句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
func main() {a := make(chan int, 1024)b := make(chan int, 1024)for i := 0; i < 10; i++ {fmt.Printf("第%d次,", i)a <- 1b <- 1select {case <-a:fmt.Println("from a")case <-b:fmt.Println("from b")default:fmt.Println("from c")}}
}

在以上代码中,同时在a和b中进行选择,哪个有内容就从哪个读,由于channel的读/写操作是阻塞操作,使用select语句可以避免单个channel的阻塞。此外,select同样可以使用default代码块,避免所有channel同时阻塞。

3、循环结构

在Go语言中,循环语句的关键字是for,没有while关键字。for语句可以根据指定的条件重复执行其内部的代码块,这个判断条件一般是由for关键字后面的子语句给出的。

a. for 语句

for循环是一个循环控制结构,可以执行指定次数的循环。循环体不停地进行循环,直到循环终止条件返回false时自动退出循环,执行for的“}”之后的语句。

for 循环控制变量初始化; 循环终止条件; 循环控制变量增量 {    循环体
}

初始语句是在第一次循环前执行的语句,一般使用初始语句执行变量初始化,如果变量在此处被声明,其作用域将被局限在这个for的范围内。

初始语句可以被忽略,但是初始语句之后的分号必须写,例如:

step := 2
for ; step > 0; step-- {    fmt.Println(step)
}

注意事项

  • 左花括号“{”必须与for处于同一行。
  • Go语言中的for循环与C语言一样,都允许在循环条件中定义和初始化变量,唯一的区别是,Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量。
  • Go语言的for循环同样支持continue和break来控制循环,但是它提供了一个更高级的break,可以选择中断具体的哪一个循环。
b. range 语句

每一个for语句都可以使用一个特殊的range子语句,其作用类似于迭代器,用于轮询数组或者切片值中的每一个元素,也可以用于轮询字符串的每一个字符,以及字典值中的每个键值对,甚至还可以持续读取一个通道类型值中的元素。

range关键字的右边是range表达式,表达式一般写在for语句的前面,以便提高代码的易读性。

range关键字的左边表示的是一对索引-值对,根据不同的表达式,返回不同的结果,range右边表达式返回的类型如下图:

range右边表达式返回的类型,除了轮询字符串外,还包括数组、切片、字典及通道等。

func main() {numbers := [5]int{1, 2, 3, 4}for i, x := range numbers {fmt.Printf("第%d次,x的值为%d\n", i, x)}
}

在以上代码中,定义了numbers的长度为5,但numbers中只有4个值,因此最后一个为空值,从for循环返回的信息可以看到第5次x的值为0,代码块的确执行了5次。

4、defer 延迟

Go语言除了传统的流程控制语句外,还有一些特殊的控制语句,defer就是其中之一。defer主要用于延迟调用指定的函数,defer关键字只能出现在函数的内部:

func main() {defer fmt.Printf("world!")fmt.Printf("Hello ")
}

在以上代码中会首先打印hello,然后打印world,因为第一句使用了defer关键字,defer语句会在函数最后执行,defer后面的表达式必须是外部函数的调用,上面的例子就是针对fmt.Println函数的延迟调用。

defer有如下两大特点:

(1)只有当defer语句全部执行,defer所在函数才算真正结束执行。

(2)当函数中有defer语句时,需要等待所有defer语句执行完毕,才会执行return语句。

因为defer的延迟特点,可以把defer语句用于回收资源、清理收尾等工作。使用defer语句之后,不用纠结回收代码放在哪里,反正都是最后执行。

需要注意defer的执行时机:

var i = 0func print() {fmt.Println(i)
}func main() {for ; i < 5; i++ {defer print()}
}

  • 在以上代码中,返回了5个5,这是因为每个defer都是在函数轮询之后才执行,此时i的值为5。

var i = 0func print(i int) {fmt.Println(i)
}func main() {for ; i < 5; i++ {defer print(i)}
}

  • 当i等于0时,defer语句第一次被压栈,此时defer后面的函数返回0;i不断自增,一直到i等于4时,defer语句第5次入栈,defer后的函数返回4。
  • Go语言会根据defer后进先出原则逐条打印栈内的数值,因此就看到了现在的结果。
5、标签

在Go语言中,有一个特殊的概念就是标签,可以给for、switch或select等流程控制代码块打上一个标签,配合标签标识符可以方便跳转到某一个地方继续执行,有助于提高编程效率。

标签的名称区分大小写,为了提高代码的易读性,建议标签名称使用大写字母和数字。标签可以标记任何语句,并不限定于流程控制语句,未使用的标签会引发错误。

a. break 语句

Go语言中的break语句主要用于以下两方面:

  • 用于循环语句中跳出循环,并开始执行循环之后的语句。
  • break可以使switch语句执行一条case后跳出语句。

在多重循环中,可以用标号label标出想跳出的指定循环:

func main() {OuterLoop:for i := 0; i < 2; i++ {for j := 0; j < 5; j++ {switch j {case 2:fmt.Println(i, j)break OuterLoopcase 3:fmt.Println(i, j)break OuterLoop}}}fmt.Println("调出循环")
}
  • 退出OuterLoop对应的循环之外,也就是跳转到最后。

b. continue 语句

Go语言中的continue语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for循环内使用,在continue语句后添加标签时,表示开始标签对应的循环:

func main() {
OuterLoop:for i := 0; i < 2; i++ {for j := 0; j < 5; j++ {switch j {case 2:fmt.Println(i, j)continue OuterLoopcase 3:fmt.Println(i, j)continue OuterLoop}}}fmt.Println("调出循环")
}
  • 第8行将结束当前循环,开启下一次外层循环,而不是第4行的循环。与break不同的是,continue表示跳转后继续执行操作。

c. goto 语句

Go语言中的goto语句通过标签进行代码间的无条件跳转,同时goto语句在快速跳出循环、避免重复退出上也有一定的作用,使用goto语句能简化一些代码的实现过程。

goto语句通常与条件语句配合使用,可用来实现条件转移、构成循环、跳出循环体等功能。但是,在结构化程序设计中一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序都产生困难。

goto语句有以下几个特点:

  • goto语句只能在函数内跳转。
  • goto语句不能跳过内部变量声明语句,这些变量在goto语句的标签语句处又是可见的。
  • goto语句只能跳到同级作用域或者上层作用域内,不能跳到内部作用域内。

使用goto退出多层循环:

func main() {for x := 0; x < 10; x++ {for y := 0; y < 10; y++ {if y == 2 {// 跳转到标签goto breakHere}}}// 手动返回,避免执行进入标签return// 标签breakHere:fmt.Println("done")
}
  • 第11行,标签只能被goto使用,但不影响代码执行流程,此处如果不手动返回,在不满足条件时,也会执行第14行代码。

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

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

相关文章

涉密网络的IP查询防护策略

涉密网络的安全性对于维护国家、企业及个人的核心利益至关重要。在当今数字化时代&#xff0c;网络攻击日益猖獗&#xff0c;其中IP查询是攻击者获取目标信息的一种常见手段。本文将探讨涉密网络中防护IP查询的关键策略&#xff0c;以确保网络的机密性和安全性。 1. 专用VPN和…

一个文件下png,jpg,jpeg,bmp,xml,json,txt文件名称排序命名

#utf-8 #authors 会飞的渔WZH #time:2023-12-13 import os# 要修改的图像所在的文件夹路径 root_path rD:\images\lines2\3 # 要修改的图像所在的文件夹路径filelist os.listdir(root_path) #遍历文件夹 print(len(filelist)) i0for item in filelist:if item.endswith(.…

《点云进阶》专栏文章目录

目录 一、PCL进阶篇* 二、Open3D进阶篇 一、PCL进阶篇 * PCL 最小二乘拟合二维直线PCL 最小二乘拟合空间直线PCL 计算点云的倒角距离&#xff08;Chamfer Distance&#xff09;PCL 点云配准精度评价——点到面的均方根误差PCL 可视化八叉树PCL 计算Hausdorff距离PCL 从变换矩…

使用 Timm 库替换 RT-DETR 主干网络 | 1000+ 主干融合RT-DETR

文章目录 前言版本差异说明替换方法parse_moedl( ) 方法_predict_once( ) 方法修改 yaml ,加载主干论文引用timm 是一个包含最先进计算机视觉模型、层、工具、优化器、调度器、数据加载器、数据增强和训练/评估脚本的库。 该库内置了 700 多个预训练模型,并且设计灵活易用。…

winform使用CefSharp嵌入VUE网页并交互

1、NuGet添加CefSharp 如果下载慢或失败可以更新下载源 腾讯资源https://mirrors.cloud.tencent.com/nuget/华为资源https://repo.huaweicloud.com/repository/nuget/v3/index.json 2、将项目平台改为X64 3、在winform窗体添加cef using CefSharp; using CefSharp.WinForms; u…

【QT】时间日期与定时器

目录 1.时间日期相关的类 2.日期时间数据与字符串之间的转换 2.1 时间、日期编辑器属性设置 2.2 日期时间数据的获取与转换为字符串 2.3 字符串转换为日期时间 3.QCaIendarWidget日历组件 3.1基本属性 3.2 公共函数 3.3 信号 4.实例程序演示时间日期与定时器的使用 …

04.HTML其他知识

HTML其他知识 1.HTML实体 介绍 在 HTML 中我们可以用一种特殊的形式的内容&#xff0c;来表示某个符号&#xff0c;这种特殊形式的内容&#xff0c;就是 HTML 实体。比如小于号 < 用于定义 HTML 标签的开始。如果我们希望浏览器正确地显示这些字符&#xff0c;我们必须在…

详解MySQL中一条SQL执行过程

MySQL基本架构 如下图所示&#xff0c;从宏观角度来说MySQL架构可以分为server层和存储引擎层&#xff0c;其中Server层包含如下: 连接器:进行身份认证和权限相关校验。查询缓存:MySQL8.0已废弃&#xff0c;查询缓存主要是用于提高查询效率而加的一层缓存。分析器:对SQL执行动…

别再错过!这些隐藏版小网站,让你大呼过瘾!

今天给大家分享一些摸鱼小网站&#xff0c;再忙也别忘记摸鱼呀~~~ 01、简约时钟 网址&#xff1a;https://dateclock.net/ github&#xff1a;https://github.com/jtheletter/dateclock 这个是github上的一个开源项目&#xff0c;大家可以看到它的全部代码。打开网站就能看到…

法语 Alt 代码表

法语的 Alt 代码表&#xff0c;请参考下图。 输入方法就是按住 Alt 键不松开&#xff0c;然后在小键盘上输入字符&#xff0c;松开 Al 键&#xff0c;计算机就能输出上面的字符了。 西班牙语 Alt 代码表 - 系统容器 - iSharkFly西班牙语 Alt 代码表&#xff0c;请参考下图。 输…

《大模型合规白皮书2023》:为了解大模型立法最新动态和立法趋势提供有价值的参考

本白皮书在我国人工智能法律监管框架下进一步梳理了大模型相关方的合规义务及要点&#xff0c;并展望未来大模型法律监管体系的发展趋势与特征&#xff0c;对政府、企业、社会共建大模型治理体系提出切实建议&#xff0c;从而为社会各界了解大模型立法最新动态和立法趋势提供有…

C++ Qt开发:Slider滑块条组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍Slider滑块条组件的常用方法及灵活运用。 当…

Docker容器:docker推送镜像至Harbor

目录 1、Harbor创建项目 2、进入test项目&#xff0c;查看推送命令 3、在docker服务器上准备一个镜像 4、修改docker客户端配置 5、重启docker服务 6、docker登录Harbor 7、docker镜像推送到Harbor 1、Harbor创建项目 2、进入test项目&#xff0c;查看推送命令 3、在dock…

css3实现动态心电图折线

css3实现动态心电图折线 M&#xff08;moveto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff0c;移动到的点的x轴和y轴的坐标L&#xff08;lineto&#xff09;&#xff1a;需要两个参数&#xff08;x轴和y轴坐标&#xff09;&#xff0c;它会在当前位置…

挺进云存储,天翼云全新一代XSSD勇立潮头

引言&#xff1a;自研高性能分布式存储引擎LAVA&#xff0c;实现云硬盘持续创新获得新突。 【全球云观察 &#xff5c; 科技热点关注】 作为算力基础设施的基石&#xff0c;云存储的发展一直备受公有云厂商所重视&#xff0c;对拉动云厂商营收规模带来重要价值&#xff0c;就…

微信开发者工具安装教程

文章目录 下载安装包执行安装包 #微信开发者工具安装教程 下载安装包 官网网址 执行安装包 D:\Program Files (x86)\Tencent\微信web开发者工具\dll

联合体(c语言)

1.联合体由一个或多个成员组成&#xff0c;这些成员可以是不同类型&#xff0c;但编译器只为最大的数据成员分配足够的内存空间&#xff0c;所有成员占一个空间&#xff0c;所以联合体也叫共用体&#xff08;可以利用这一点用不同的变量表示同一快空间)&#xff0c;给其中一个成…

Git应用——代码提交规范 feat ,fix ,style

当前使用 feat 增加新功能fix 修复问题/BUGstyle 代码风格相关无影响运行结果的perf 优化/性能提升refactor 重构revert 撤销修改test 测试相关docs 文档/注释chore 依赖更新/脚手架配置修改等workflow 工作流改进ci 持续集成types 类型定义文件更改wip 开发中 别处看到 fea…

椋鸟C语言笔记#26:数据在内存中的存储(大小端字节序)、浮点数的存储(IEEE754)

萌新的学习笔记&#xff0c;写错了恳请斧正。 目录 大小端字节序 什么是大小端 写一个判断大小端的程序 浮点数在内存中的存储&#xff08;IEEE 754规则&#xff09; 引入 存储规则解释 读取规则解释 1.阶码不全为0或全为1&#xff08;规格化数&#xff09; 2.阶码全为…

IDEA Maven项目如何引用本地jar包,并打包发布

jar包位于当前路径下的lib目录中 引入所需要的配置 查看当前jar包的相关信息 包的引入,需要使用到当前包的artifactId, groupId, version 需要到包的/META-INF/maven/ 下面的 pom.xml 文件里面找 在Maven构建项目时&#xff0c;生成的依赖包中的/META-INF/maven目录存放了一些…