当前位置: 首页 > news >正文

【golang】for语句和switch语句

使用携带range子句的for语句时需要注意哪些细节?

numbers1 := []int{1, 2, 3, 4, 5, 6}
for i := range numbers1 {if i == 3 {numbers1[i] |= i}
}
fmt.Println(numbers1)

这段代码执行后会打印出什么内容?

答案:[1 2 3 7 5 6]

当for语句被执行的时候,在range关键字右边的numbers1会先被求值。

这个位置上的代码被称为range表达式。range表达式的结果值可以是数组、数组的指针、切片、字符串、字典或者允许接收操作的通道中的某一个,并且结果值只能有一个。

对于不同种类的range表达式结果值,for语句的迭代变量的数量可以有所不同。

就拿我们这里的numbers1来说,它是一个切片,那么迭代变量就可以有两个,右边的迭代变量代表当次迭代对应的某一个元素值,而左边的迭代变量则代表该元素值在切片中的索引值。

稍稍修改一下上面的代码

numbers2 := [...]int{1, 2, 3, 4, 5, 6}
maxIndex2 := len(numbers2) - 1
for i, e := range numbers2 {if i == maxIndex2 {numbers2[0] += e} else {numbers2[i+1] += e}
}
fmt.Println(numbers2)

打印的内容会是什么?

答案:[7 3 5 7 9 11]

需要注意两点:

  1. range表达式只会在for语句开始执行时被求值一次,无论后边会有多少次迭代;
  2. range表达式的求值结果会被复制,也就是说,被迭代的对象是range表达式结果值的
    副本而不是原值。

基于这两个规则,我们接着往下看。在第一次迭代时,我改变的是numbers2的第二个元素的值,新值为3,也就是1和2之和。

但是,被迭代的对象的第二个元素却没有任何改变,毕竟它与numbers2已经是毫不相关的两个数组了。因此,在第二次迭代时,我会把numbers2的第三个元素的值修改为5,即被迭代对象的第二个元素值2和第三个元素值3的和。

以此类推,之后的numbers2的元素值依次会是7911。当迭代到最后一个元素时,我会把numbers2的第一个元素的值修改为16之和。

我们再次修改代码

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

把numbers2的值由一个数组改成一个切片,其中的元素值都不变。为了避免混淆,把这个切片值赋给变量numbers3,并且把后边代码中所有的numbers2都改为numbers3。

执行这段代码后打印的内容会是什么呢?

答案:[22 3 6 10 15 21]

切片与数组是不同的,切片是引用类型的,而数组是值类型的,所以上述第二段和第三段代码的结果是不同的,具体原因之前文章有提到–数组和切片底层原理。

switch语句中的switch表达式和case表达式之间有着怎样的联系?

先看一段代码:

value1 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch 1 + 3 {
case value1[0], value1[1]:fmt.Println("0 or 1")
case value1[2], value1[3]:fmt.Println("2 or 3")
case value1[4], value1[5], value1[6]:fmt.Println("4 or 5 or 6")
}

case表达式一般由case关键字和一个表达式列表组成,表达式列表中的多个表达式之间需要有英文逗号,分割,比如,上面代码中的case value1[0], value1[1]就是一个case表达式,其中的两个子表达式都是由索引表达式表示的。

在这里的每个case子句中的那些打印语句,会分别打印出不同的内容,这些内容用于表示case子句被选中的原因,比如,打印内容0 or 1表示当前case子句被选中是因为switch表达式的结果值等于0或1中的某一个。

拥有这样三个case表达式的switch语句可以成功通过编译吗?如果不可以,原因是什么?如果可以,那么该switch语句被执行后会打印出什么内容。

switch语句switch表达式的结果类型,以及各个case表达式中子表达式的结果类型都是有要求的。毕竟,在 Go 语言中,只有类型相同的值之间才有可能被允许进行判等操作。

如果switch表达式的结果值是无类型的常量,比如1 + 3的求值结果就是无类型的常量4,那么这个常量会被自动地转换为此种常量的默认类型的值,比如整数4的默认类型是int,又比如浮点数3.14的默认类型是float64。

因此,由于上述代码中的switch表达式的结果类型是int,而那些case表达式中子表达式的结果类型却是int8,它们的类型并不相同,所以这条switch语句是无法通过编译的。

再来看一段很类似的代码:

value2 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch value2[4] {
case 0, 1:fmt.Println("0 or 1")
case 2, 3:fmt.Println("2 or 3")
case 4, 5, 6:fmt.Println("4 or 5 or 6")
}

switch表达式的结果值是int8类型的,而那些case表达式中子表达式的结果值却是无类型的常量了。这与之前的情况恰恰相反。那么,这样的switch语句可以通过编译吗?

答案是肯定的。因为,如果case表达式中子表达式的结果值是无类型的常量,那么它的类型会被自动地转换为switch表达式的结果类型,又由于上述那几个整数都可以被转换为int8类型的值,所以对这些表达式的结果值进行判等操作是没有问题的。

当然了,如果这里说的自动转换没能成功,那么switch语句照样通不过编译。

image.png

由于需要进行判等操作,所以switch表达式case表达式的子表达式的结果类型需要相同。

switch语句会进行有限的类型转换,但肯定不能保证这种转换可以统一它们的类型。还要注意,如果这些表达式的结果类型有某个接口类型,那么一定要小心检查它们的动态值是否都具有可比性(或者说是否允许判等操作)。

switch语句对它的case表达式有哪些约束?

switch语句在case子句的选择上是具有唯一性的。

正因为如此,switch语句不允许case表达式中的子表达式结果值存在相等的情况,不论这些结果值相等的子表达式,是否存在于不同的case表达式中,都会是这样的结果。具体请看这段代码:

value3 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch value3[4] {case 0, 1, 2:fmt.Println("0 or 1 or 2")case 2, 3, 4:fmt.Println("2 or 3 or 4")case 4, 5, 6:fmt.Println("4 or 5 or 6")
}

由于在这三个case表达式中存在结果值相等的子表达式,所以这个switch语句无法通过编译。不过,好在这个约束本身还有个约束,那就是只针对结果值为常量的子表达式。

比如,子表达式1+1和2不能同时出现,1+3和4也不能同时出现。有了这个约束的约束,我们就可以想办法绕过这个对子表达式的限制了。再看一段代码:

value5 := [...]int8{0, 1, 2, 3, 4, 5, 6}
switch value5[4] {case value5[0], value5[1], value5[2]:fmt.Println("0 or 1 or 2")case value5[2], value5[3], value5[4]:fmt.Println("2 or 3 or 4")case value5[4], value5[5], value5[6]:fmt.Println("4 or 5 or 6")
}

变量名换成了value5,但这不是重点。重点是,我把case表达式中的常量都换成了诸如value5[0]这样的索引表达式。

虽然第一个case表达式和第二个case表达式都包含了value5[2],并且第二个case表达式和第三个case表达式都包含了value5[4],但这已经不是问题了。这条switch语句可以成功通过编译。

不过,这种绕过方式对用于类型判断的switch语句(以下简称为类型switch语句)就无效了。因为类型switch语句中的case表达式的子表达式,都必须直接由类型字面量表示,而无法通过间接的方式表示。代码如下:

value6 := interface{}(byte(127))
switch t := value6.(type) {
case uint8, uint16:fmt.Println("uint8 or uint16")case byte:fmt.Printf("byte")
default:fmt.Printf("unsupported type: %T", t)
}

量value6的值是空接口类型的。该值包装了一个byte类型的值127。在后面使用类型switch语句来判断value6的实际类型,并打印相应的内容。

这里有两个普通的case子句,还有一个default case子句。前者的case表达式分别是case uint8, uint16和case byte。你还记得吗?byte类型是uint8类型的别名类型。

因此,它们两个本质上是同一个类型,只是类型名称不同罢了。在这种情况下,这个类型switch语句是无法通过编译的,因为子表达式byte和uint8重复了。

文章学习自郝林老师的《Go语言36讲》

相关文章:

【golang】for语句和switch语句

使用携带range子句的for语句时需要注意哪些细节? numbers1 : []int{1, 2, 3, 4, 5, 6} for i : range numbers1 {if i 3 {numbers1[i] | i} } fmt.Println(numbers1)这段代码执行后会打印出什么内容? 答案:[1 2 3 7 5 6] 当for语句被执行…...

三、数据库索引

1、索引介绍 索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。 常见的索引结构有:B数,B树,Hash和红黑树等。在MySQL中,无论是 InnoDB还是MyISAM,都使用了B树作为索引…...

长时间带什么耳机最舒服,分享长时间佩戴舒服的耳机推荐

时代在进步,科技在不断革新。近年来,一种崭新的耳机——骨传导耳机,如火如荼地进驻耳机市场,引起一阵热潮。不论是平日里的工作出勤还是运动时的挥洒汗水,相比传统耳机,骨传导耳机无疑更加贴合现代生活的需…...

Yolov8小目标检测(1)

💡💡💡本文目标:通过原始基于yolov8的红外弱小目标检测,训练得到初版模型,进行问题点分析; 💡💡💡Yolo小目标检测,独家首发创新(原创),适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,带你轻松实现小目标检测涨点 💡💡…...

GPS定位漂移问题分析

有很多种因素会影响到GPS的准确率,以下是一个GPS误差引入简表: l 卫星时钟误差:0-1.5米 l 卫星轨道误差:1-5米 l 电离层引入的误差:0-30米 l 大气层引入的误差:0-30米 l 接收机…...

前端简介(HTML+CSS+JS)

学习Django过程中遇到一些前端相关的内容,于是整理了一下相关概念。 前端开发是创建WEB页面或APP等前端界面呈现给用户的过程。 如果只是想要入门前端,只要学习网页三剑客(HTML、CSS、JavaScript)即可。 如果把网页比喻成一个房子,HTML就是…...

List与String数组互转

一.List 转为 String 数组 1.使用toArray方法 public static void main(String[] args) {List<String> list Lists.newArrayList("1","2","3");// Java6以前版本String[] str1 list.toArray(new String[list.size()]);// Java6以后版本…...

MySQL中的数据类型

文章目录 1 常见的数据类型2 整数类型2.1 属性 M2.2 属性 UNSIGNED2.3 属性 ZEROFILL2.4 整数类型的适用场景 3 浮点类型4 定点类型5 位类型6 日期与时间类型6.1 YEAR 类型6.2 DATE 类型6.3 TIME 类型6.4 DATETIME 类型6.5 TIMESTAMP 类型 1 常见的数据类型 类型类型分类整数类…...

python多任务

​ 一、多任务 1.1 概念 多任务就是指&#xff1a;同一时间能执行多个任务。比方我们的电脑能一边QQ聊天&#xff0c;一边写论文&#xff0c;还能听歌。 1.2 多任务的优势&#xff1a; 多任务的最大好处是 充分利用CPU资源&#xff0c;提高程序的执行效率。 1.3 多任务的两种表…...

c语言 - inline关键字(内联函数)

概念 在编程中&#xff0c;inline是一个关键字&#xff0c;用于修饰函数。inline函数是一种对编译器的提示&#xff0c;表示这个函数在编译时应该进行内联展开。 内联展开是指将函数的代码插入到调用该函数的地方&#xff0c;而不是通过函数调用的方式执行。这样可以减少函数调…...

如何在Ubuntu 18.04上安装PHP 7.4并搭建本地开发环境

引言 PHP是一种流行的服务器脚本语言&#xff0c;用于创建动态和交互式web页面。开始使用你选择的语言是学习编程的第一步。 本教程将指导您在Ubuntu上安装PHP 7.4&#xff0c;并通过命令行设置本地编程环境。您还将安装依赖管理器Composer&#xff0c;并通过运行脚本来测试您…...

狭义相对论

文章目录 一、为什么光速不变&#xff1f;二、为什么爱因斯坦坚信“相对性原理”三、逻辑和数学显威力&#xff0c;狭义相对论时空变换&#xff08;洛伦兹变换&#xff09;推导四、新时空变换带来的新时空观1、有关相对论时间的“傻问题”2、关于相对论的“怪问题”3、关于“双…...

仓库使用综合练习

目录 1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 2、安装搭建私有仓库 Harbor 3、编写Dockerfile制作Web应用系统nginx镜像&#xff0c;生成镜像nginx:v1.1&#xff0c;并推送其到私有仓库。 4、Dockerfile快速搭建自己专属的LAMP环境&#xff0c;生…...

如何在前端实现WebSocket发送和接收TCP消息(多线程模式)

目录 第一步&#xff1a;创建WebSocket连接第二步&#xff1a;监听WebSocket事件第三步&#xff1a;发送消息第四步&#xff1a;后端处理函数说明 当在前端实现WebSocket发送和接收TCP消息时&#xff0c;可以使用以下步骤来实现多线程模式。本文将详细介绍如何在前端实现WebSoc…...

VB.NET通过VB6 ActiveX DLL调用PowerBasic及FreeBasic动态库

前面说的Delphi通过Activex DLL同时调用PowerBasic和FreeBasic写的DLL&#xff0c;是在WINDOWS基础平台上完成的。 而 .NET平台是架在WINDOWS基础平台之上的&#xff0c;它的上面VB.NET或C#等开发的APP程序&#xff0c;下面写一下用VB.NET&#xff0c;通过VB6注册的Activex DLL…...

怎样不引入图片实现前端css实现x关闭按钮

首先初始化一个dom节点 <span class"closeButton"></span>设置样式 .closeButton {width: 12px;height: 12px;margin-top: 5px;margin-right: 5px;float: right;cursor: pointer;color: #105c86;}通过伪元素before after画两条线 margin-left 的设置是…...

SpringBoot实现文件下载的多种方式

SpringBoot实现文件下载的几种方式 1. 将文件以流的形式一次性读取到内存&#xff0c;通过响应输出流输出到前端1.1 下载文件 2. 将输入流中的数据循环写入到响应输出流中&#xff0c;而不是一次性读取到内存&#xff0c;通过响应输出流输出到前端3. 下载网络文件到本地4. 网络…...

uniapp简单版语音播放

mounted() {this.ScanAudio(http://118.178.137.235:88/ipoker.mp3, 3); // 开始播放音频},ScanAudio(url, count) {// 递归终止条件&#xff1a;当循环次数小于等于 0 时&#xff0c;停止递归if (count < 0) return;// 创建内部音频上下文对象var music uni.createInnerAu…...

前端三剑客入门一文解决

文章目录 HTML快速开发网站Flask页面结构标签基础标签超链接图片列表下拉框表格input系列多行文本form表单 网络请求HTML案例 CSSCSS盒模型CSS样式定义CSS选择器 CSS样式使用1. 在标签上直接写2. 在head标签中写3.写到css文件中 标签样式1. 高度和宽度2. 块级和行内标签3.字体设…...

php curl apache 超时 500错误

web请求超过40s 就返回500错误 php的超时时间 set_time_limit无效 curl CURLOPT_TIMEOUT 设置请求时间 无效 设置apache Timeout 链接超时 无效 最后添加 Fcgid才可以 apache 配置文件 httpd.conf <IfModule mod_fcgid.c>FcgidProcessLifeTime 10000FcgidIOTimeout 1000…...

ctf show web 入门43

打开靶场代码逻辑如下&#xff1a; if(!preg_match(“/\ |/|cat/i”, $c)) 它过滤了三个关键内容&#xff1a; \ (空格)&#xff1a;你不能直接在命令中使用空格&#xff08;例如 ls -l 或 cat flag 都会失败&#xff09;。 / (正斜杠)&#xff1a;你不能使用路径符号&#xf…...

通用AGI终极范式:从多模态感知到意识涌现的统一理论(世毫九实验室原创研究)

通用AGI终极范式&#xff1a;从多模态感知到意识涌现的统一理论作者&#xff1a;方见华单位&#xff1a;世毫九实验室摘要本研究基于世毫九理论体系的数学框架&#xff0c;构建了通用人工智能&#xff08;AGI&#xff09;的完整理论体系和演化路径。通过建立包含拓扑复杂度、动…...

AI创业从模型竞赛到场景落地:2026年生态爆发与实战指南

1. 从HumanX 2026归来&#xff1a;我眼中的AI创业生态爆发图景刚从HumanX 2026的会场回来&#xff0c;整个人还沉浸在那种高速迭代、热气腾腾的氛围里。如果你问我最大的感受是什么&#xff0c;我会毫不犹豫地说&#xff1a;AI创业的“场景化落地”竞赛&#xff0c;已经进入了白…...

Casbin Talent 2026:高校开发者开源进阶与工业级项目实战指南

1. 项目概述&#xff1a;Casbin Talent 2026&#xff0c;一个为高校开发者量身定制的开源进阶通道如果你是一名在校大学生&#xff0c;对开源世界充满好奇&#xff0c;渴望在真实的工业级项目中打磨技术&#xff0c;但又觉得像Google Summer of Code&#xff08;GSoC&#xff0…...

Zotero茉莉花插件:3大功能轻松管理中文文献,科研效率翻倍提升

Zotero茉莉花插件&#xff1a;3大功能轻松管理中文文献&#xff0c;科研效率翻倍提升 【免费下载链接】jasminum A Zotero add-on to retrive CNKI meta data. 一个简单的Zotero 插件&#xff0c;用于识别中文元数据 项目地址: https://gitcode.com/gh_mirrors/ja/jasminum …...

GD32F303硬件I2C实战:手把手教你用AT24C02 EEPROM存储和读取设备配置参数

GD32F303硬件I2C实战&#xff1a;构建工业级参数存储系统 在嵌入式设备开发中&#xff0c;系统参数的持久化存储是个看似简单却暗藏玄机的需求。想象一下&#xff0c;当你的智能温控器经历突然断电后&#xff0c;所有用户设置的日程和偏好全部归零——这种体验足以让产品口碑崩…...

MATLAB 2018a/2023b实测:Libsvm安装后如何用自带数据集快速验证与跑通第一个模型

MATLAB 2018a/2023b实战&#xff1a;Libsvm安装后快速验证与模型跑通全流程 当你第一次在MATLAB中成功安装Libsvm后&#xff0c;那种兴奋感可能很快会被"接下来该做什么"的迷茫所取代。别担心&#xff0c;这篇文章将带你用Libsvm自带的heart_scale数据集&#xff0c;…...

Matlab实战:基于EGM2008模型与球谐函数解析全球重力梯度场

1. 地球重力场模型与EGM2008简介 地球重力场是描述地球质量分布的重要物理场&#xff0c;它影响着卫星轨道、海平面变化甚至我们日常使用的导航系统。想象一下&#xff0c;如果把地球比作一个表面凹凸不平的土豆&#xff0c;重力场就是描述这个"土豆"各处引力大小的地…...

Android 14 + Linux 6.1 平台 RTL8822CE Wi‑Fi 适配实战:从 PCI 已枚举到成功扫描热点

摘要 在 Android 14 Linux 6.1 的移植过程中&#xff0c;RTL8822CE Wi‑Fi 很容易出现一种“硬件已经被 PCI 枚举到&#xff0c;但系统就是没有 wlan0”的尴尬状态。本文复盘一次完整的 RTL8822CE 适配过程&#xff0c;最终定位出两个连续阻塞点&#xff1a;第一&#xff0c;目…...

Qt WebEngine(02):从架构到实战,构建现代桌面Web混合应用

1. Qt WebEngine架构解析&#xff1a;为什么它适合桌面混合开发 第一次接触Qt WebEngine时&#xff0c;我正为一个工业控制面板项目头疼——需要同时展示实时设备数据和远程监控页面。传统方案要么用浏览器插件&#xff08;兼容性噩梦&#xff09;&#xff0c;要么自己实现HTTP…...