在本教程中,你将学习Go编程语言中关于运算符的所有知识。那么,什么是运算符,它们为什么重要呢?让我通过一个例子来解释。最简单的运算符例子就是加号(+)运算符。它接收两个数字,将它们相加,并返回它们的和。因此,运算符用于对值进行操作并返回另一个值。这对于Go语言中的所有运算符,或者说编程中的运算符都是如此。所有运算符都接收两个参数并返回一个结果。正如Go语言运算符规范所说:运算符将操作数组合成表达式。
谈到规范,如果你查看这些规范,你会发现一大段看起来像物理学博士论文的文字。但不用担心!我会以更简单易懂的方式来解析它们。让我们开始吧!
Go语言中的运算符
Go编程语言中的所有运算符可以分为7类:
算术运算符 位运算符 赋值运算符 比较运算符 逻辑运算符 地址运算符 接收运算符
在深入了解这些类别之前,让我们首先定义一下什么是运算符。
运算符的定义
为了说明它们的功能和工作原理,让我们快速预览一下第一个算术运算符,即加号(+)运算符。
var r = 21 + 21 // 使用'+'运算符
fmt.Println(r) // 输出:42
在这个例子中,加号运算符用于将两个值相加并返回其结果。但运算符也可以用于变量。
var a = 40
var b = 2
var r1 = 40 + 2 // 运算符作用于两个值
var r2 = a + 2 // 运算符作用于一个变量和一个值
var r3 = a + b // 运算符作用于两个变量
这应该能让你清楚地理解Go语言中的运算符是如何工作的。
算术运算符
算术运算符基本上是对两个数值进行数学运算。
// 加法运算符:'+'
// 将两个值相加
var a = 40 + 2 // 42
// 减法运算符:'-'
// 从一个值中减去另一个值
var a = 40 - 2 // 38
// 乘法运算符:'*'
// 将两个值相乘
var a = 40 * 2 // 80
// 除法运算符:'/'
// 将一个值除以另一个值
var a = 40 / 2 // 20
// 取模运算符:'%'
// 返回除法的余数
var a = 40 % 2 // 0
// 自增运算符:'++'
// 将变量的值增加1
var a = 42 // 42
a++ // 43
// 自减运算符:'--'
// 将变量的值减少1
var a = 42 // 42
a-- // 41
以上例子中,前四个运算符如果你学过小学数学应该很容易理解。但我想稍微谈谈其他三个,因为它们经常出现,我认为需要更多的解释。
取模运算符 %
通常用于需要对特定值集进行操作或任务时。一个简单的例子是为数据表中的行着色。你知道,这些大而复杂的类似Excel的表格有成千上万的行和列。为了让它们看起来不那么“刺眼”,可以用不同的颜色显示每隔一行。而实现这一点的运算符就是取模运算符。
var colorIndex = rowNr % 2 // 0 或 1,取决于 rowNr 的值
var color = colors[colorIndex] // 如果 colorIndex=0 则为白色,如果 colorIndex=1 则为浅蓝色
这是 %
运算符一个非常简单但广泛使用的用例。
自增和自减运算符 ++
和 --
通常用于计数。例如,如果你想统计邮件发送的次数。
var emailCounter = 0
sendEmail() {
// 发送邮件的代码
...
emailCounter++
}
当我们进入循环时,你会经常看到这些运算符。但这是另一个文章的话题。
位运算符
接下来,我们有位运算符。老实说,当编写包含大量业务逻辑的Web应用程序时,你可能不会像其他运算符那样经常使用位运算符。但快速了解它们仍然有意义。特别是因为我可以证明7 AND 2等于2。
首先,我们需要明确位运算符的作用。位运算符作用于操作数的位表示。简单来说,位运算符并不作用于数值本身,而是作用于表示该数值的二进制位。
a = 0 0 0 0 0 0 1 0 // 数字2的二进制表示
当我说位运算符作用于位表示时,我的意思是,运算符不作用于值2本身,而是作用于表示数字2的0和1。
现在让我们谈谈这些运算符究竟是如何工作的。想象一下有三排6个灯开关,一排在另一排的下面。你可以将位运算符想象成根据第一排和第二排的特定规则来翻转第三排的开关。例如,AND运算符只有在第一排和第二排同一位置的开关都打开时,才会让第三排的开关打开。
// 1=开,0=关
row1: 1 0 0 1 0 0
row2: 0 0 0 1 0 1
row3: 0 0 0 1 0 0 // row1 AND row2 的结果
现在,让我们再次回顾一下解释句:位运算符作用于操作数的位表示。看,现在一切都说得通了。让我们看看所有不同的位运算符:
var a int = 7 // 位表示:0000 0111
var b int = 2 // 位表示:0000 0010
// 位与运算符:'&'
// 如果两个位都是1,则为1,否则为0
var c = a & b // 0000 0010
// 位或运算符:'|'
// 如果两个位都是0,则为0,否则为1
var c = a | b // 0000 0111
// 位异或运算符:'^'
// 如果只有一个位是1,则为1,否则为0
var c = a ^ b // 0000 0101
// 位与非运算符:'&^'
// 先进行与运算,然后取反
var c = a &^ b // 0000 0101
// 二进制左移运算符:'<< X'
// 从右侧推入X个0
var c = a << b // 0001 1100 <- 从右侧推入2个0
// 二进制右移运算符:'>> X'
// 从左侧推入X个0
var c = a >> b // 0000 0001 <- 从左侧推入2个0
我知道这些运算符可能有点让人困惑(双关语)。幸运的是,你几乎不会用到它们。但正如我提到的,第一个例子证明了7 AND 2实际上是2。我将留给你去证明这一点。
赋值运算符
接下来,我们有赋值运算符。顾名思义,它们将值赋给变量(谁能想到)。赋值运算符通常只是较长表达式的简写版本。你将在例子中看到这一点:
// 为变量赋值
var a int = 40
// 将一个值加到变量上
// 并将结果赋给同一个变量
// 等同于 a = a + 40
a += 40
// 从变量中减去一个值
// 并将结果赋给同一个变量
// 等同于 a = a - 40
a -= 40
// 将变量乘以一个值
// 并将结果赋给同一个变量
// 等同于 a = a * 40
a *= 40
// 将变量除以一个值
// 并将结果赋给同一个变量
// 等同于 a = a / 40
a /= 40
// 将变量除以一个值
// 并将余数赋给同一个变量
// 等同于 a = a % 40
a %= 40
我认为这些运算符很容易理解。不幸的是,Golang也有位赋值运算符。同样,你几乎不会遇到它们,但我会在这里提一下以供参考。
// 我们需要一个变量来开始
var a int = 40
// 将 & 运算符应用于 'a' 和 40
// 然后将结果赋给 'a'
// 等同于 a = a & 40
a &= 40
// 其他位赋值运算符的工作方式相同
a |= 40 // 等同于 a = a | 40
a ^= 40 // 等同于 a = a ^ 40
a <<= 40 // 等同于 a = a << 40
a >>= 40 // 等同于 a = a >> 40
比较运算符
比较运算符很容易理解。它们结合两个或多个条件并评估结果。例如,如果你想检查两个变量是否具有相同的值,你可以使用等于运算符 ==
。
var a int = 4
var b int = 2
a == b // false
在编写代码时,比较运算符是你的基本工具。没有一天会不需要检查 userId == 42
、superSafePassword == "admin"
或 followedThisAuthorOnMedium == true
。所以一定要熟记它们。
var a int = 4
var b int = 2
// 等于运算符:'=='
// 检查两个操作数是否相同
a == b // false
// 不等于运算符:'!='
// 检查两个操作数是否不同
a != b // true
// 大于运算符:'>'
// 检查第一个操作数是否大于第二个操作数
a > b // true
// 小于运算符:'<'
// 检查第一个操作数是否小于第二个操作数
a < b // false
// 大于或等于运算符:'>='
// 检查第一个操作数是否大于或等于第二个操作数
a >= b // true
// 小于或等于运算符:'<='
// 检查第一个操作数是否小于或等于第二个操作数
a <= b // false
逻辑运算符
在某种意义上,逻辑运算符与位运算符相似,但它们不是作用于操作数的位表示,而是作用于布尔(bool)语句。因此,它们用于确定变量和值之间的逻辑结果。
由于这是一篇关于Golang运算符的文章,我不会解释编程中的逻辑是如何工作的。如果你想了解更多关于这方面的信息,只需搜索“编程真值表”,你会找到大量信息。
Go语言中的逻辑运算符如下所示:
var a bool = true
var b bool = false
// 逻辑与运算符:'&&'
// 如果两个语句都为真,则为真
a && b // false
// 逻辑或运算符:'||'
// 如果其中一个语句为真,则为真
a || b // true
// 逻辑非运算符:'!'
// 如果语句为假,则为真,反之亦然
!a // false
!b // true
在编写Go应用程序时,你会到处看到逻辑运算符,特别是与比较运算符结合使用。业务逻辑通常涉及很多“如果这样,那么那样,但只有在这也为真时”。所以,像这样的代码在Golang应用程序中很常见:
var userIsRegistered bool = user.IsRegistered // true
var userRole string = user.Role // "Admin"
var hasWriteAccess bool = (userIsRegistered == true) && (userRole == "Admin" || userRole == "Editor")
在编写业务逻辑时,逻辑运算符将是你最常用的工具之一。因此,你应该花些时间学习它们的工作原理。
在我们查看最后的运算符之前,我必须提醒你。它们并不是真正“适合初学者”的运算符。所以,如果你是编程新手,可以跳过它们。当我们谈论指针和通道时,你会看到这些运算符及其工作原理。
但如果你是一位熟悉指针和类似概念的经验丰富的程序员,请继续阅读。
地址运算符
Go语言有两个运算符可以让你与指针交互,即“地址”运算符 &
和“解引用”或“间接”运算符 *
。首先,让我们从“地址”运算符开始:
var i int = 42
// 地址运算符:'&'
// 获取变量的内存地址
var p *int = &i // 获取 i 的内存地址
fmt.Println(i) // 输出:42
fmt.Println(p) // 输出:0xc0000a4040
这里发生了很多神奇的事情。第一部分很简单:我们定义了一个新的int变量并用值42初始化它。接下来的部分有点复杂。使用 &
运算符,我们检索变量 i
的内存地址,并将其存储到一个新的指针类型变量 p
中。这意味着变量 p
保存了变量 i
的内存地址,而不是值42!
要实际检索内存地址的值,你需要使用“解引用”或“间接”运算符 *
。
var i int = 42
var p *int = &i // 获取 i 的内存地址
// 解引用运算符:'*'
// 获取指针变量的值
var j int = *p // 解引用 p 的内存地址
fmt.Println(j) // 输出:42
接收运算符
最后一个我们要讨论的运算符是接收运算符 <-
,它作用于通道类型。接收操作 <-ch
的值是从通道 ch
接收到的值。你可以使用这个运算符将值发送到通道中,或者从中读取值。让我向你展示在Golang代码中这是什么样的。
需要注意的是:我将使用一些Go的高级特性。所以,如果你不完全理解,也没关系。正如我所说,这些最后的运算符并不是真正适合初学者。
// 定义一个发送函数
// 它将一个整数值发送到通道中
func send(ch chan int, v int) {
// 将值发送到通道中
ch <- v
}
// 创建一个新的整数通道
ch := make(chan int)
// 在协程中调用发送函数
go send(ch, 42)
// 从通道中读取值
v := <-ch
fmt.Println(v) // 输出:42
现在,让我们逐行分析这些代码以使其更清晰。再次说明,由于这是关于Golang运算符的教程,我不会详细介绍其他内容。如果你想了解更多关于通道和协程的概念,我将在未来撰写相关文章。
第一行定义了一个函数,该函数将一个整数值发送到通道中。接下来的几行创建了一个用于整数值的通道。然后,我们在协程中调用这个函数,传递创建的通道和值42。最后,我们从通道中读取一个值,并将其存储到变量 v
中。
我想提醒你注意接收运算符的方向:
ch <- v // 将数据放入通道
v := <-ch // 从通道读取数据
一个非常重要的注意事项:接收运算符 <-
是一个阻塞运算符,这意味着它会暂停代码执行,直到另一端准备好。这种等待允许协程在不使用互斥锁的情况下同步数据。
结论
Go语言中的运算符是每个开发者都需要了解的。是的,有些运算符有点特殊,你可能在日常工作中永远不会使用它们。但其他运算符在编写Golang代码时将成为你的基本工具。
如果你喜欢这篇文章,请继续关注以获取更多与Go相关的教程和文章。
点击关注并扫码添加进交流群
免费领取「Go 语言」学习资料