《Go语言实战笔记》3 表达式

86 阅读3分钟

3.1 保留字

image.png

3.2 运算符

image.png

优先级

一元运算符优先级最高,二元(两个操作数)则分成五个级别,从高低分别是 image.png

二元运算符

除位移操作外,操作数类型必须相同。如果其中一个是无显式类型声明的常量,那么该 常量操作数会自动转型。

位运算符

image.png

自增

自增、自减不再是运算符。只能作为独立语句,不能用于表达式。

指针

不能将内存地址与指针混为一谈。

内存地址是内存中每个字节单元的唯一编号,而指针则是一个实体。指针会分配内存空间,相当于一个专门用来保存地址的整型变量。

  • 取址运算符“&”用于获取对象地址。
  • 指针运算符“*”用于间接引用目标对象。
  • 二级指针**T,如包含包名则写成*package.T。

指针类型支持相等运算符,但不能做加减法运算和类型转换。如果两个指针指向同一地 址,或都为nil,那么它们相等。

可通过unsafe.Pointer将指针转换为uintptr后进行加减法运算,但可能会造成非法访问。

Pointer类似C语言中的void*万能指针,可用来转换指针类型。它能安全持有对象或对象成员, 但uintptr不行:后者仅是一种特殊整型,并不引用目标对象,无法阻止垃圾回收器回收对象内存。

指针没有专门指向成员的“->”运算符,统一使用“.”选择表达式。

3.3 初始化

对复合类型(数组、切片、字典、结构体)变量初始化时,有一些语法限制。

  • 初始化表达式必须含类型标签。
  • 左花括号必须在类型尾部,不能另起一行。
  • 多个成员初始值以逗号分隔。
  • 允许多行,但每行须以逗号或右花括号结束。
package main

import "fmt"

func main() {
	type data struct {
		x int
		y string
	}
	var a data = data{1, "s"}
	b := data{
		1,
		"asdf",
	}
	c := []int{
		1,
		2,
		3,
	}
	d := []int{1, 2, 3,
		4,
		5,
	}
	fmt.Println(a, b, c, d)

}

3.4 流程控制

if

支持初始化语句,可定义块局部变量或执行初始化函数。

func main() {
	x := 10
	if err := check(x); err != nil {
		log.Fatalln(err)
	}
	x++
	println(x)
}

switch

与if语句类似,switch语句也用于选择执行,但具体使用的场景会有所不同。

  • switch同样支持初始化语句,按从上到下,从左到右顺序匹配case执行,只有全部匹配失败时才会执行default块
  • 考虑到default与else类似,建议将其放在switch末尾
  • 相邻的空case不构成多条件匹配
  • 不能出现重复的case值:
  • 无需显示执行break语句,case执行完毕后制动中断。如需贯通后续case(源码顺序),须执行fallthrough,但不再匹配后续条件表达式
package main

func main() {
	switch x := 5; x {
	default:
		println(x)
	case 5:
		x += 10
		println(x)		//15
		fallthrough		// 继续执行下一case,但不再匹配表达式
	case 6:
		x += 20
		println(x)		//35
		//fallthrough		//cannot fallthrough final case in switch
	}
}

  • 某些时候,switch语句还被用于替换if语句。被省略的switch条件表达式默认值为true,继而与case比较表达式结果匹配

for

仅有for一种循环语句

    for i := 0; i < 3; i++ {
    }
    for x < 10 {    //类似 while x < 10
        x++
    }
    for {           //类似while true
        break
    }

可用for…range完成数据迭代,支持字符串、数组、数组指针、切片、字典、通道类型,返回索引、键值数据

package main

import (
	"fmt"
)

func main() {
	s := "asdfsdf"
	for i, j := range s {
		fmt.Println(i, j)
	}
	a := [3]string{"a", "b", "c"}
	for i, j := range a {
		fmt.Println(i, j)
	}
	b := []string{"x", "x", "x", "y"}
	for i, j := range b {
		fmt.Println(i, j)
	}
	e, f, g := 1, 2, 3
	d := [3]*int{&e, &f, &g}
	for i, j := range d {
		fmt.Println(i, j)
	}
	var h = make(map[string]int)
	h["a"] = 1
	h["b"] = 2
	h["c"] = 3
	h["d"] = 4
	for i, j := range h {
		fmt.Println(i, j)
	}

}

允许返回单值,或用“_”忽略。

package main

import (
	"fmt"
)

func main() {
	data := [3]string{"a", "b", "c"}
	for i := range data { // 忽略值
		fmt.Println(i)
	}
	for _, j := range data { // 忽略索引
		fmt.Println(j)
	}
	for range data { //仅迭代,不返回。可用来执行清空channel等操作

	}

}


goto,continue,break

  • goto 使用goto前,须先定义标签。标签区分大小写,且未使用的标签会引发编译错误。
func main() {
	for i := 0; i < 3; i++ {
		println(i)
		if i >= 1 {
			goto exit
		}
	}
exit:
	println("exit.")
}
0
1
exit.

不能跳转到其他函数,或内层代码块内

  • break: 用于switch、for、select语句,终止,整个语句块执行。
  • continue:仅用于for循环,终止后续逻辑,立即进入下一轮循环

配合标签,break,continue可在多层嵌套中指定目标层级

func main() {
outer:
	for x := 0; x < 5; x++ {
		for y := 0; y < 10; y++ {
			if y > 2 {
				println()
				continue outer
			}
			if x > 2 {
				break outer
			}
			print(x, ":", y, " ")
		}
	}
}
结果
0:0 0:1 0:2
1:0 1:1 1:2
2:0 2:1 2:2