Golang_3_表达式
三、表达式
3.1 保留字
25个关键字
3.2 运算符
乘幂和绝对值,对应的是 math库
里的Pow
和Abs
函数
优先级
从高到低:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
相同优先级从左到右
二元运算符
除了位移运算,操作类型必须相同。若有无显式类型,那么这个操作数会自动转型
位移的右边操作数必须是无符号整数
位运算符
AND 位与:全一则一 a & b 0101 & 0011 = 0001
OR 位或:有一则一 a | b 0101 | 0011 = 0111
XOR 位异或:不进位加法 a ^ b 0101 ^ 0011 = 0110
NOT 非或反 ^a ^0101 = 1010
AND NOT 位清除(bit clear) a &^ b 0110 &^ 1011 = 0100
bit clear
和异或不同,位清除将左右操作数对应二进制位都为1的重置为0,从而达到一次清除多个标记位的目的
可以先 ^
再 &
a 0101 0101
b ^ 0011 &^ 0011
c = 0110 =0100
1100 &^ 0100 = 1000
其实就是把 b 中为 1 的位,在a中进行清除,所以上方0101最后一位被清除
自增
不再是运算符,只能作为独立语句,不能用于表达式
func main(){
a := 1
++a // ++不能前置
// 错误,不能作为表达式使用
if(a++) > 1{
}
p := &a
*p++
}
表达式 通常是求值代码,可以作为 右值 或 参数 使用
语句 完成一个行为
表达式可以作为一个语句用,但是语句不能作为表达式
指针
内存地址和指针不能混为一谈
- 内存地址是每个内存单元的唯一编号
- 指针是一个实体,会分配内存空间,实质上是一个保存地址的整型变量
p := &x | … | x := 100 | |
---|---|---|---|
内存值 | 0x1200 | … | 100 |
实际地址 | 0x800 | … | 0x1200 |
&
取地址符用于 获取目标地址*
指针运算符用于间接引用目标对象**T
二级指针,包名则写成*package.T
*
指针运算符在左边时,用于更新目标对象的状态
*
指针运算符在右边时,用于获取目标状态
x := 10
// 定义指针变量
var p *int = &x
// 左边:还原地址p对应值,操作目标对象加10
*p += 10
// 右边,获取目标对象
fmt.Println(x, p, *p)
// 输出 20 0xc420016090 20
指针只支持 ==
,不能作加减法。(两个指针都指向一个地址或都为nil,那么它们相等)
指针没有 ->
运算符,统一使用 .
a := struct {
x int
}{}
a.x = 100
长度为0的对象,依然时“合法存在的”,拥有其内存地址,而不是 nil
3.3 初始化
复合类型(数组、切片、字典、结构体)初始化,有一些语法限制:
- 初始化表达式必须包含类型标签
- 左花括号
{
不能另起一行 - 多个初始值用
,
分隔 - 允许多行,但是必须用
,
或}
结尾
type data struct {
x int
s string
}
var a data = data{
1,
"123",
}
var a data = {1, "123",} // 错误一,没有写类型标签 data
var a data = data{// 错误二,写多行的时候,末尾必须要是.或}
1,
"123"
}// 错误
var a data = data{1,"123"} // 正确,写一行的时候,末尾逗号可以不加
//也就是说,末尾必须是 , 或 } ,多行的时候 } 拿上去也是正确的,建议写标准
3.4 流控制
if…else…
- 条件表达式必须是布尔类型
- 可省略括号,左
{
不能另起一行 - else 必须写在
}
后面
可以对局部变量进行初始化
x := 3
if xinit(); x == 0{
fmt.Println("a")
}
// 可以定义多个局部变量
if a, b := x+1, x+10; a < b{
fmt.Println(a)
} else {
fmt.Println(b)
}
尽量少写死代码(永远不会执行的语句),减少嵌套,让 正常的逻辑处于相同层次
==P46==的几个样例描述得比较具体
switch
-
不用写
break
,默认就是break
,执行完自动跳出,如果要往下,需要加上fallthrough
,直接执行后续case
内语句,且不用匹配case
后的条件(按照源码的顺序) -
case
的条件不能重复出现 -
default
是最后执行的,不管写在前面还是后面。建议是写在最后
switch x := 5; x{
default:
fmt.Println(x) // 输出15
case 5:
x += 10
fmt.Println(x) // 输出35
fallthrough
case 6:
x += 20
fmt.Println(x)
// 这里如果加 fallthrough 会出错,因为后面没有case了
}
通常 fallthrough
必须放在case
块的尾部,可以使用 break阻止
switch x := 5; x {
case 5:
x += 10
fmt.Println(x)
if x >= 15 {
break
}
// case必须是case内的最后一句
fallthrough
case 6:
x+=20
fmt.Println(x)
}
多个条件是 or
关系,case x>0 && x<=5 :
和 case x>0, x<=5:
for循环
只有For一种循环控制,while也使用for来实现
// 实现 while(x < 10) ||| for ; x < 10; {}
for x < 10 {
x++
}
// 实现 while true{} ||| for true {}
for{
break
}
可以使用 for…range 实现迭代,支持 字符串、数组、数组指针、切片、字典、通道类型,返回索引、键值数据。
func main(){
data := [3]string{"a", "b", "c"}
for i, s := range data {
fmt.Println(i, s)
}
} // 输出 0 a
// 1 b
// 2 c
允许返回单值,使用 _
忽略
data := [3]string{"a", "b", "c"}
for i := range data {
fmt.Print(i) // 输出 012
}
for _, s := range data {
fmt.Print(s) // 输出 abc
}
无论是for
循环还是range
,局部变量都会重复使用,这其实会对闭包产生影响,后面会介绍到
data := [3]string{"a", "b", "c"}
for i, s := range data {
fmt.Println(&i, &s)
}
// 输出
// 0xc420016090 0xc42000e1d0
// 0xc420016090 0xc42000e1d0
// 0xc420016090 0xc42000e1d0
==P55==
goto、continue、break
goto
使用的时候需要定义标签,然后goto
,且定义的标签必须要使用- 不能跳转到其他函数,或内层代码块
- continue、break 和常用的功能一样,跳到下个循环条件 和 跳出循环