Golang_3_表达式

三、表达式

3.1 保留字

25个关键字

3.2 运算符

image-20190917222326127

乘幂和绝对值,对应的是 math库 里的PowAbs函数

优先级

从高到低:

* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||

相同优先级从左到右

二元运算符

除了位移运算,操作类型必须相同。若有无显式类型,那么这个操作数会自动转型

位移的右边操作数必须是无符号整数

位运算符

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 和常用的功能一样,跳到下个循环条件 和 跳出循环

分享: