全网最全!操作符使用细节全解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 全网最全!操作符使用细节全解析

1.操作符概览

Go 语言提供了丰富的操作符来操纵变量和执行程序逻辑。掌握这些操作符的语法和功能,是编写和理解 Go 代码的基础。

可以将 Go 语言的操作符主要分为以下几大类

分类 包含的操作符
算术操作符 +、-、*、/、%、++、--等
关系操作符 ==、!=、>、<、>=、<=等
逻辑操作符 \&\&、||、!等
位操作符 &、
赋值操作符 =、+=、-=、&=、|=等
其他操作符 &(取地址)、*(根据地址取值)、.(结构体字段访问)等

1.1 算术操作符

算术操作符用于执行基本的数学运算,例如加法、乘法等。

支持的算术操作符如下

操作符 描述 实例
+ 加法 x + y
- 减法 x - y
* 乘法 x * y
/ 除法 x / y
% 求余 x % y
++ 自增,变量值加 1 x++
-- 自减,变量值减 1 x--

用一个简单的算术运算实例,来展示它们的基本用法


package main
import "fmt"
func main() {    x := 10    y := 3        // 基本算数运算     fmt.Println(x + y) // 13    fmt.Println(x - y) // 7      fmt.Println(x * y) // 30        // 除法和取余    fmt.Println(x / y) // 3    fmt.Println(x % y) // 1        // 自增,自减    x++    fmt.Println(x) // 11    x--    fmt.Println(x) // 10 }

1.2 关系操作符

关系操作符用于比较两个值的大小关系,比较的结果为布尔类型,true 表示成立,false 表示不成立。

Go 语言支持的关系操作符如下

操作符 描述 实例
== 等于,相同则为真 x == y
!= 不等于,不同则为真 x != y
> 大于,左操作数值>右操作数值则为真 x > y
< 小于,左操作数值<右操作数值则为真 x < y
>= 大于等于 x >= y
<= 小于等于 x <= y

使用关系操作符进行比较运算的示例


package main
import "fmt"
func main() {    x := 10    y := 3        // 相等与不等    fmt.Println(x == y) // false    fmt.Println(x != y) // true        // 大于与小于        fmt.Println(x > y)  // true    fmt.Println(x < y)  // false        // 大于等于与小于等于    fmt.Println(x >= y) // true    fmt.Println(x <= y) // false}

1.3 逻辑操作符

逻辑操作符用于逻辑运算,最终结果是一个布尔值。

Go 语言支持以下逻辑操作符

操作符 描述 实例
&& 逻辑 AND,两边都为真才为真 x && y
|| 逻辑 OR,两边任一为真则为真 x || y
! 逻辑 NOT,真为假,假为真 !x

使用示例:


package main
import "fmt"
func main() {    x := true    y := false        // 逻辑与运算    fmt.Println(x && y) // false        // 逻辑或运算     fmt.Println(x || y) // true        // 逻辑非运算    fmt.Println(!x) // false}


 

2. 操作符使用细节

Go 语言的各类操作符及基本使用方法。

2.1 算术操作符类型支持

算术操作符可以处理多种类型的数据,同时也有一些使用上的限制。

1.整数

Go 语言中的整型可以进行所有算术及位运算。操作数为整数时,遵循数学运算的实数域规则:


package main 
import "fmt"
func main() {    var int1 int = 10    var int2 int32 = 3    var res int        // 支持不同整型混合运算     res = int1 + int2      fmt.Println(res)        // 支持取余运算    res = int1 % int2     fmt.Println(res)        // 结果类型由操作数类型决定    fmt.Printf("%T\n", res) //int32}

需要注意的是,不同整数类型混合运算时,结果类型由操作数中的较大整数类型确定。

2.浮点数

Go 语言支持两种浮点数类型:float32 和 float64。浮点数运算遵循数学运算规则,但存在精度损失问题:


package main
import (      "fmt"    "math")
func main() {    // math包定义浮点数常量    var f1 = math.Pi     var f2 = math.E        // 支持基本算术运算    fmt.Println(f1 + f2)        // 小数可能存在精度损失    fmt.Println(f1 - f2) }

3.复数

complex64 和 complex128 两种复数类型也可以进行基本的加减乘除运算:


package main
import (   "fmt"   "math/cmplx")
func main() {    // 定义两个复数    var c1 = 1 + 2i    var c2 = 3 + 4i        // 支持算术运算    fmt.Println(c1 + c2)     fmt.Println(c1 - c2)}

需要注意复数同实数混合运算是不被支持的。

2.2 关系操作符的类型检查

关系操作符支持比较多种类型,但也存在一定的限制。

1. 基本数据类型

整数、浮点数、字符等基本类型直接使用关系操作符 执行 比较:


package main
import "fmt"
func main() {    var int1 = 1    var int2 = 2        // 比较整数    fmt.Println(int1 > int2) // false        var float1 = 1.2    var float2 = 2.3        // 比较浮点数    fmt.Println(float1 < float2) // true        var char1 = 'a'    var char2 = 'b'        // 比较字符    fmt.Println(char1 != char2) // true}

2. 数组及切片

数组和切片不能直接比较,需要循环比较元素才能判断关系:


package main
import "fmt"
func main() {    slice1 := []int{1, 2, 3}    slice2 := []int{1, 2, 4}        // 直接比较切片是非法的    // fmt.Println(slice1 == slice2)        // 循环比较切片元素    for i := 0; i < len(slice1); i++ {        if slice1[i] != slice2[i] {            fmt.Println("slice1 != slice2")            return        }    }        fmt.Println("slice1 == slice2") }

3. 指针

指针之间可以使用 == 和 != 操作符进行比较,比较的是指针的值(内存地址):


package main import "fmt" func main() {   var ptr1, ptr2 *int   var num = 100       ptr1 = &num   ptr2 = &num       // 比较指针地址   if ptr1 == ptr2 {       fmt.Println("Pointers are equal")    }}

需要注意的是 Go 语言中没有指针运算,不能对指针进行算术比较。

2.3 逻辑操作符的短路效果

逻辑操作符 && 和 || 具有短路效果, 也就是说如果通过第一个操作数就能确定表达式的值,则不会再计算第二个操作数。

利用这个特点,可以简化代码:


package main
func testShortCircuit() bool {    println("testShortCircuit called")     return true;}
func main() {      // 使用逻辑与运算短路    if testShortCircuit() && testShortCircuit() {       println("do something")    }}

在上例中,因为 && 是逻辑与,如果第一个操作数返回 false,则整个表达式结果一定为 false,不会再调用第二个操作数。

类似原理,也可利用逻辑或的短路效应简化代码。

2.4 利用位操作进行状态控制

Go 语言内建的位操作符使其可以轻松实现状态控制这样的操作:


package main
const (    Open = 1 << iota    Close    Pending)
var state = Open | Pending 
func main() {    // 测试状态     if state&Open == Open {        println("Open")    }        if state&Close == Close {        println("Close")     }        // 修改状态    state &^= Pending    println(state)}

用对比、与(&)、或(|)、异或(^)等位操作,可以有效控制程序状态。

2.5 赋值操作符的链式应用

Go 语言的赋值操作符比较特殊,它支持链式操作:


package main  
import "fmt"
func main() {      // 链式赋值操作    x, y := 1, 2    x, y = y, x+y        fmt.Println(x, y)}

这样一行代码就交换了 x 与 y 的值。链式赋值顺序为从右到左。

利用这个特性,可以编写非常简洁的交换变量代码。同理,也可以实现链式增减等操作。


 

3. Go 语言操作符的运算顺序

在表达式中出现多个操作符时,运算的顺序会直接影响结果。Go 语言定义了一套完整的运算顺序规则。

3.1 操作符优先级表

当存在不同优先级的操作符组合时,Go 按照以下表中从上到下优先级顺序计算:

分类 操作符
一元操作符 +、-、!、^等
乘除法 *、/、%、<<、>>等
加减法 +、-
关系运算 ==、<、<=等
逻辑与 &&
逻辑或 ||

在同一优先级中,按照操作符出现顺序从左至右计算。

看一个具体的运算顺序示例:


package main import "fmt" func main() {    x := 6 + 3*4/2 - 1    fmt.Println(x) //11        y := true || false && true     fmt.Println(y) //true}

示例按照上表优先级规则从高到低顺序计算,很好解释结果。

3.2 利用括号调整优先级

如果想调整某个子表达式的优先级,可利用括号来明确控制运算顺序:


package main
import "fmt"
func main() {  x := (3 + 4) * 5    fmt.Println(x) //35        y := 3 + 4*5     fmt.Println(y) //23        z := true && (false || true)    fmt.Println(z) //true}

类似数学约定,添加括号可以提高该组运算优先级,先行计算。

3.3 赋值运算通常最后运行

对于组合表达式,Go 语言保证所有的赋值运算通常都是最后运行:


package main
func main() {    x, y := 2, 3        // +会先运行,然后再赋值     a := x + y      println(a)        // /也先运行        b := x / y      println(b)}

这符合预期表达式应该先运算而后取值的思维逻辑。

学会利用括号调整运算顺序,并注意每个操作符优先级,可以避免很多意外情况的出现。


 

4. 操作符实战示例

理解了基本操作符的用法之后,来看一些实际应用场景下的操作符代码示例,让这些抽象的概念具象化,加深理解。

这些示例覆盖字符串、位运算、复数等多个方面,都是可运行的完整代码。

4.1 利用关系运算符处理字符串

利用关系运算符,可以优雅实现字符串开头或结尾的判断:


package main
import "fmt" 
func main(){   var str = "Hello World"      // 判断以Hello开头   if "Hello" == str[0:5] {       fmt.Println("string starts with Hello")   }      // 判断以orld结尾   if "orld" == str[len(str)-5:] {       fmt.Println("string ends with orld")     } }

再如判断字符串包含或不包含某子串也可以利用运算符实现:


package main
import (    "fmt"    "strings")
func main() {    var str = "Hello World"        // 判断是否包含某子串    if strings.Contains(str, "World") {        fmt.Println("string contains World")    }        // 判断是否不包含    if !strings.Contains(str, "foo") {        fmt.Println("string does not contain foo")     }}

通过合理灵活运用关系运算符,可以使代码更简洁易读。

4.2 位操作实现集合运算

Go 语言的位操作符可以实现高效的集合运算。可构建如下的使用示例


package main
func main() {    var A = 4 // 100    var B = 6 // 110        // 并集     var union = A | B        println(union) // 110        // 交集    var intersect = A & B         println(intersect) // 0        // 差集     var diff = A &^ B        println(diff) // 100}

对比关系运算符,位操作符可以快速判断两个集合的包含和交叉情况。

再如根据特征位确定对象状态等也可以利用位操作实现。

4.3 复数的基本运算

Go 语言内置对复数的支持。可进行一些基本的复数运算


package main 
import (    "fmt"    "math/cmplx")
func main() {    // 定义两个复数     var c1 = 1 + 2i    var c2 = 3 + 4i        // 复数支持加减乘除运算    fmt.Println(c1 + c2)    fmt.Println(c1 - c2)    fmt.Println(c1 * c2)        // 取复数的modulus    fmt.Println(cmplx.Abs(c1)) }

输出结果:


(4+6i)(-2-2i)(-5+10i)2.23606797749979

Go 语言内置的复数支持让开发者可以方便处理类似信号处理、图像处理等需要域运算的场景。


 

5. 操作符的最佳实践

通过上面内容的详细介绍,已全面了解了 Go 语言中操作符的种类、语法细节和实战应用等内容。

将尝试总结归纳操作符的最佳实践和使用经验。

5.1 采用简单明了的风格

虽然 Go 语言提供了大量操作符供选择,但在实践中, 仍要注意采用简单明了的风格:

优先考虑基本的算术、逻辑、关系运算符,避免使用过于花哨的位运算符

注意合理利用操作符的优先级和关联性,减少不必要的括号

多用常量,变量命名表达业务意图,不要依赖运算符实现业务

避免多行复杂的运算表达式,分解为简单的语句

比如实现条件判断,推荐:


if x > 0 && matchType(y) {   ... }

不推荐:


if (0 < x) && (&TYP1 == (*int)(&y)) {  ...}

针对不同场景采用不同的风格可以提高代码可读性。

5.2 注意操作符之间的交互作用

同时使用多个操作符时,要注意它们之间可能存在的交互作用:

比较运算与 nil 值的关系需要注意

位运算可以实现判断状态,但复杂时难懂

新旧变量值的交换,要留意计算顺序

譬如交换两个值需要注意赋值 优先级


x, y = y, x // 错误
tmp := x; x = y; y = tmp // 正确

5.3 合理利用操作符简化代码

熟练掌握操作符的相关技巧,可以合理简化代码:

& 取地址和 * 取值访问指针,可以避免 副本

关系运算符判断起始,可以减少重复代码

复合赋值运算简化重复计算

逻辑运算短路实现优雅分支

一元运算符结合 defer 优雅获取错误

比如 defer 方式获取错误信息:


func f() error {    var err error    defer func() {        if r := recover(); r != nil {            err = r.(error) // 将panic转换为错误        }    }()
   //业务逻辑   ...      return err  }


 

6. 总结

运算是编程语言的重要组成部分。Go 语言提供全部的操作符对变量进行运算和操作。

这些操作符涵盖算术、逻辑、位运算、赋值等各个方面。熟练掌握这些操作符的语法及细节非常必要。

同时编写代码过程中,运用这些操作符解决实际问题可以大大提高效率。不过需要注意一些最佳实践,避免常见的陷阱。

希望这篇文章可以帮助读者全面了解 Go 语言操作符的世界,更好地编写灵活、高效的 Go 代码。

目录
相关文章
|
6月前
|
编译器 C语言
操作符详解(C语言基础深入解析)
操作符详解(C语言基础深入解析)
|
3月前
|
JavaScript 前端开发 开发者
深入解析JavaScript中的比较操作符
【8月更文挑战第20天】
27 0
|
6月前
|
存储 程序员 C语言
“探索C语言操作符的神秘世界:从入门到精通的全方位解析“
“探索C语言操作符的神秘世界:从入门到精通的全方位解析“
|
存储 Unix 编译器
猿创征文|C语言操作符(运算符)一万多字的详细解析
猿创征文|C语言操作符(运算符)一万多字的详细解析
|
PHP 开发者
范围解析操作符(类常量访问)|学习笔记
快速学习范围解析操作符(类常量访问)
范围解析操作符(类常量访问)|学习笔记
Rxjava源码解析笔记 | 剖析map、flatmap操作符的使用与联系
Rxjava源码解析笔记 | 剖析map、flatmap操作符的使用与联系
|
编译器 C语言 索引
【C语言】 操作符的困惑都在这里解决! (操作符详细解析)
本文是C语言操作符详解篇,可以帮助新手小白,快速了解c语言操作符的使用方法,每一个操作符都有列子和解释,做以参考学习
149 0
【C语言】 操作符的困惑都在这里解决! (操作符详细解析)
RxJava2-map操作符源码解析
RxJava2的map操作符用于对输入对象进行转换。 map操作图 下图所示为将String的输出转化为Integer的场景。 String转Integer Map的源码解析如下,首先涉及到以下几个类: 1、Observable:被观察者,通过Observable.create创建一个被观察者,即观察者模式里面的主题Subject对象。
1084 0
|
程序员 C++
《深入理解C++11:C++ 11新特性解析与应用》——3.4 显式转换操作符
本节书摘来自华章计算机《深入理解C++11:C++ 11新特性解析与应用》一书中的第3章,第3.4节,作者 IBM XL编译器中国开发团队,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1473 0
|
程序员 C++
《深入理解C++11:C++ 11新特性解析与应用》——2.6 noexcept修饰符与noexcept操作符
本节书摘来自华章计算机《深入理解C++11:C++ 11新特性解析与应用》一书中的第2章,第2.6节,作者 IBM XL编译器中国开发团队,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1734 0

推荐镜像

更多