重学Go语言 | 运算符与控制结构

语言: CN / TW / HK

我正在参加「掘金·启航计划」

对于任何编程语言来说,运算符和控制结构都算是最基础的知识了,既然是基础,当然非常有必要学习,因此在这篇文章中我们就来讨论一下。

运算符

运算符的作用是将操作数组合成表达式,比如下面的代码中,我们通过赋值和加号组成了两个表达式:

var i,j = 1,2 n := i + j

Go的运算符大体分为六种:算术运算符关系运算符逻辑运算符位运算符赋值运算符指针运算符

算术运算符

| 运算符 | 含义 | | ------ | ---------------------------------------------------------- | | + | 加号,除了用于整数,浮点数,复数外,还可以用于字符串的拼接 | | - | 减号 | | * | 相乘 | | / | 相除 | | % | 求余,只能用于整数 | | ++ | 自增 | | -- | 自减 | | + | 正数,注意与加号(+)的区别 | | - | 负数,注意与减号(-)的区别 |

用法示例:

``` var str1 string = "hello" var str2 string = "world" str := str1 + str2 //使用+号拼接字符串

i := 3.2 % 2 //报错,只能对整数求余

var n int = 1 n++ ++n //错误,自增只能加了操作数后面,自减也是同样的 ```

关系运算符

通过逻辑运算符组成的表达式,其计算结果为布尔值,一般用于控制结构的条件部分:

| 运算符 | 含义 | | ------ | ---------- | | == | 相等 | | != | 不相等 | | <= | 小于或等于 | | < | 小于 | | >= | 大于或等于 | | > | 大于 |

用法示例:

if 2 == (1 + 1) { fmt.Println("相等") }

逻辑运算符

逻辑运算符组成的表达式,其计算结果也同样蝇布尔值,因此也用于控制结构的条件部分:

| 运算符 | 含义 | | ------ | ------------------------------------ | | && | 逻辑与 | | \|\| | 逻辑或 | | ! | 逻辑非,一元运算符,具有较高的优先级 |

位运算符

位运算符只能用于整数

| 运算符 | 含义 | | ------ | -------------------------------------------------------- | | & | 按位与,两个操作数都1的位置为1,否为0 | | \| | 按位或,两个操作数只要有1的位置,都为1,否则为0 | | ^ | 按位异或,两个操作数都相同为0,否则为1 | | << | 按位左移 | | >> | 按位右移 | | &^ | 按位清空,根据右边操作数为1的位置,将左边对应位置设为0。 |

用法示例:

``` fmt.Println(2 & 1) // 00000010 & 00000001,可以看出,没有哪个位置两个操作都为1,因此结果为:00000000 fmt.Println(2 | 1) // 00000010 & 00000001,结果为00000011,也就是3 fmt.Println(2 ^ 1) // 00000010 & 00000001,结果为00000011,也就是3

fmt.Println(1 << 1) //00000001 => 00000010 fmt.Println(2 >> 1) //00000010 => 00000001

fmt.Println(23 &^ 5) 00010111 &^ 00000101 => 00010010 ```

赋值运算符

| 运算符 | 含义 | | ------ | ------------------------------------------------------------ | | = := | 赋值 | | += | 先将左侧与右侧的操作数相加,再赋值给左边的变量 | | -= | 先将左侧与右侧的操作数相减,再赋值给左边的变量 | | *= | 先将左侧与右侧的操作数相乘,再赋值给左边的变量 | | /= | 先将左侧与右侧的操作数相除,再赋值给左边的变量 | | %= | 先将左侧与右侧的操作数求余,再赋值给左边的变量 | | <<= | 先将左侧的操作数按右侧的操作数向左位移,再将位移结果赋给左边的变量 | | >>= | 先将左侧的操作数按右侧的操作数向右位移,再将位移结果赋给左边的变量 | | &= | 先将左侧与右侧的操作数进行按位与计算,再将计算结果赋给左边的变量 | | != | 先将左侧与右侧的操作数进行按位或计算,再将计算结果赋给左边的变量 | | ^= | 先将左侧与右侧的操作数进行按异或计算,再将计算结果赋给左边的变量 |

指针运算符

| 运算符 | 含义 | | ------ | ---------------------- | | & | 获取变量在内存中的地址 | | * | 声明指针变量 |

运算符的优先级

Go的++与--运算符作用于操作数时形成的是表达式,因此不纳入运算符的优先级中。

在Go语言中,一元运算符具有更高的优先级,如+(正数)-(负数)!(取反)*(指针声明)&(取址)

而赋值运算符的优先级最低,除了一元运算符以及赋值运算符外,剩下的运算符可以划分为五个优先等级:

| 优先级 | 运算符 | | ------ | --------------------------- | | 5 | * / % << >> & &^ | | 4 | + - \| ^ | | 3 | == != < <= >= > | | 2 | && | | 1 | \|\| |

控制结构

Go的控制结构包括if语句、for语句和switch语句三种。

If

if语句用于判断某个条件是否满足,如果满足,则执行if语句中的代码块,如果不满足,则忽略if语句中的代码块并继续向后执行。

最简单的if语句结构为:

if boolean expression { // do something }

其中boolean expression为一个可以得到布尔值的表达式,当布尔值为true,会执行if语句中的代码块,如:

if 2 < 10 { fmt.Println("ok") }

除了用于判断的boolean expression外,if也可以包含一个初始化表达式:

if initialization;boolean expression{ // do something }

这种情况下,if会先执行初始化表达式,之后再判断boolean expression得到的布尔是否为true

if i = 10;i < 100 { fmt.Println("ok") }

if语句后面也可以跟上else语句,当然if条件不满足时,会执行else语句中的代码块:

if boolean expression{ // do something }else{ // do something }

用法示例:

if i = 11;i < 11{ fmt.Println("ok") }else{ fmt.Println("bad") }

如果有多个分支条件判断,可以在if语句后面跟上多个else if 语句,最后可以跟上else语句,当所有条件都不满足时,会执行else语句中的代码块:

if boolean expression1 { // do something } else if boolean expression2 { // do something else } else if boolean expression3 { // do something else }else { // catch-all or default }

For

for语句用于根据条件循环执行其中的代码块,最简单的for语句格式如下:

for condition { //do something }

condition为一个可得到布尔值的表达式,Go语言中并没有while或者do-while语句,因此这种方式的用法接近其他编程语言的while或者do-while语句:

x := 1 for x < 20{ fmt.Println(x) x++ }

如果condition为空,那么此时for则为死循环:

for { //do something }

for最经典,在其他编程语言也有的用法是下面这种形式:

for init statement;condition;post statement{ //do something }

用法示例:

for i := 0; i< 10 ;i++ { fmt.Println(i) }

另外,for语句还与关键字range配合,可以用于遍历数组、map和切片等,其作用类似PHP中的foreach语句:

for k,v := range array { //do something }

用法示例:

``` var a [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}

for index,value := range a { fmt.Println(index,value) } ```

使用break关键字结束循环

for i := 0; i < 10; i++ { if i == 5 { break } fmt.Println(i) }

使用continue结束跳过单次循环:

for i := 0;i<10;i++{ if i == 5 { continue } fmt.Println(i) }

Switch

Switch与if类似,用于根据条件执行满足条件的代码块,但其用法与if不同,switch有几种不同的用法:

第一种使用方法会将switch后面的值与case后面所跟的值进行比较,满足条件则执行case中的代码块,如果都不满足,则执行default中的代码块,其结构如下所示:

switch var1 { case val1: ... case val2: ... default: ... }

用法示例:

var x = 8 switch x { case 8 : fmt.Println("8") case 9 : fmt.Println("9") case 10 : fmt.Println("10") default : fmt.Println("not found") }

从上面的例子可以看出,在满足某个条件后,switch执行完该分支就会退出switch语句,不需要像其他编程语言一样使用break来退出switch语句。

如果不想退出switch语句,需要继续让switch语句往下执行,可以在case语句内使用fallthrough关键词:

var x = 8 switch x { case 8 : fmt.Println("8") fallthrough case 9 : fmt.Println("9") fallthrough case 10 : fmt.Println("10") default : fmt.Println("not found") }

上面语句在匹配到case 8:之后,碰到了fallthrough语句,所以继续往下执行,接着继续碰到fallthrough语句,再继续执行,因此三个case代码块都会被执行。

switch的另一种用法是将后面的变量省略,而把执行的判断条件放在case关键词后面,这个用法与if/elseif语句类似:

switch { case condition1: ... case condition2: ... default: ... }

用法示例:

x := 10 switch { case x >= 10: fmt.Println("10") case x > 11: fmt.Println("11") default: fmt.Println("not found") }

switch后面可以跟上一个初始化语句:

switch initialization { case condition1: ... case condition2: ... default: ... }

用法示例:

switch x := 10; { case x >= 10: fmt.Println("10") case x > 11: fmt.Println("11") default: fmt.Println("not found") }

type-switchswitch语句的另一种用法,主要用于类型断言,后续在学习接口(interface)再介绍

小结

总结一下,这篇文章主要讲了三点:

  1. Go支持的运算符:

  2. 算术运算符

  3. 关系运算符
  4. 逻辑运算符
  5. 赋值运算符
  6. 位运算符
  7. 指针运算符

  8. 运算符的优化级

  9. Go支持的控制结构:
  10. If语句
  11. For语句
  12. Switch语句