第三章 Go語言表達式
[toc]
運算符
- 二元運算符:
除位操作之外,操作數據類型必須相同。如果其中一個是無顯式類型聲明的常量,那麼該常量就會自動轉型。代碼實驗如下:package main import "fmt" func main(){ const v = 20 var a byte = 10 b := v + a fmt.Printf("%T,%v\n",b,b) //此時b的類型爲int8 const c float32 = 3.14 d := c + v fmt.Printf("%T,%v",d,d) //此時d的類型是float32 }
指針
不能將內存地址與指針混爲一談,內存地址是內存中的每個字節單元的唯一編號;指針是一個實體;指針會分配內存空間,相當於一個專門用來 保存地址的整型變量
- 取址運算符:
&
用於獲取對象地址 - 指針運算符:
*
用於間接引用目標對象 - 二級指針:
**T
,如果包含包名的話則寫成*package.T
package main import "fmt" func main(){ x := 10 var p *int = &x fmt.Printf("%v\n%v",*p,p) }
指針類型支持相等運算符,但不能做加減法運算或者類型轉換。兩個指針執行同一個地址,或者都爲nil。那他們相等。
初始化
對於複合類型(數組,切片,字典,結構體)變量進行初始化時,有一些語法限制,如下:
- 初始化表達式必須包含標籤
- 左花括號必須在類型尾部,不能另起一行
- 多個成員初始值以逗號分隔
-
允許多行,但是每行必須用逗號或者花括號結束
正確的初始化栗子如下:package main import "fmt" func test1() { type data struct { x int y string } var a data = data{123, "abc"} //初始化 b := data{ 234, "bcd", } c := []int{ 1, 2, } d := []int{1, 23, 4, 233, 5, 12, 22, } fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) } func main() { test1() }
流控制
條件判斷類
if else:
package main import "fmt" func xinit(){ fmt.Println("heheheh") } func If_else(){ x := 10 if xinit();x == 0{ fmt.Println("hehehe") } if a,b := x+1,x+10;a < b{ fmt.Println("a < b") }else { fmt.Println("error!") } } func main() { If_else() }
- 儘量減少代碼塊嵌套,讓正常邏輯處於相同層次
package main import ( "fmt" "errors" "log" ) func check(x int) error { if x <=0 { return errors.New("x <= 0") } return nil } func main() { //If_else() //If_else2() x := 10 if err := check(x);err == nil{ x++ fmt.Println(x) }else { log.Fatal(err) //如果x<0的話會輸出錯誤日誌 } }
main可以改造成如下的樣子,以便查看
func main() { x := 100 if err := check(x);err !=nil{ log.Fatal(err) } x++ fmt.Println(x) }
- 如果在多個條件中使用局部變量,那麼只能保留原層次,或者直接使用外部變量
package main import ( "strconv" "log" "fmt" ) func main(){ s := "9" n,err := strconv.ParseInt(s,10,64) //將字符串轉換成數字 if err != nil{ log.Fatal(err) }else if n < 0 || n > 10{ log.Fatalln("invalid number!!") } fmt.Println(n) fmt.Println(err) }
- 對於某些複雜的組合條件,就需要改成函數的形式
package main import ( "strconv" "log" "fmt" ) func main(){ s := "9" if n,err := strconv.ParseInt(s,10,64);err != nil || n <0 || n > 10{ log.Fatalln("invalid num!!") } fmt.Println("ok") //條件判斷外層,不能引用條件參數 }
通過函數調用改進的版本:
package main import ( "strconv" "errors" "log" "fmt" ) func check(s string) error { n ,err := strconv.ParseInt(s,10,64) if err != nil || n <0 || n > 10{ return errors.New("invalid number!") } return nil } func main(){ s := "8" if err := check(s);err != nil{ log.Fatalln(err) } fmt.Println("ok") }
switch
- 選擇其中的一個分支執行
package main import "fmt" func main(){ switch x := 5;x { default: x+=100 fmt.Println(x) case 5: x+=50 fmt.Println(x) } }
- 選擇其中的一個分支執行
- fallthrougth:繼續執行下一個case,但是不匹配下一個case的條件
package main import "fmt" func main(){ switch x:=5;x{ default: fmt.Println(x) case 5: x += 10 fmt.Println(x) fallthrough //繼續執行下一個case,但是不匹配條件 /*if x >= 15 { break //直接跳出循環 } fallthrough*/ case 6: x += 20 fmt.Println(x) } }
- 多條件switch
package main
import (
"fmt"
)
func test1(){
switch x:=5;x{
default:
fmt.Println(x)
case 5:
x += 10
fmt.Println(x)
fallthrough //繼續執行下一個case,但是不匹配條件
case 6:
x += 20
fmt.Println(x)
}
}
func test2(){
switch x:=5;x {
case 5:
x += 10
fmt.Println(x)
if x >= 15 {
break
}
fallthrough
case 6:
x += 20
fmt.Println(x)
}
}
func test3(){
switch x:=5; {
case x > 5:
fmt.Println("a")
case x > 0 && x <= 5:
fmt.Println("b")
default:
fmt.Println("z")
}
}
func main(){
test1()
test2()
test3()
}
### 循環
#### for
只有一種循環語句,支持常用的方式:
``` go
package main
import "fmt"
func count() int{
fmt.Println("count.")
return 3
}
func main(){
for i,c :=0,count();i < c;i++{
fmt.Println("a",i)
}
c := 0
for c < count(){
fmt.Println("b",c)
c++
}
}
- 循環數組,切片,字典要用for range;返回單值的話用_
package main func main(){ data := [3]string{"a","b","c"} for i,s := range data{ println(i,s) } for i,_ range data{ println(i) //返回單值 } }
- 無論是for還是for range,其定義的局部變量會重複使用
package main func main(){ data := [3]string{"a","b","c"} for i,s := range data{ println(&i,&s) } }
-
range會複製目標數據,直接受影響的是數組;可以改用切片或者數組指針類型
package main import ( "fmt" ) func main(){ data := [3]int{10,20,30} for i,x := range data{ if i == 0 { data[0] += 100 data[1] += 200 data[2] += 300 } fmt.Printf("x: %d\tdata: %d\n",x,data[i]) //經過複製的x,值不變 } for i,x := range data[:]{ if i == 0 { data[0] += 100 data[1] += 200 data[2] += 300 } fmt.Printf("x: %d\tdata: %d\n",x,data[i]) //循環的切片,x發生了變化 } }
相關數據類型中,字符串,切片基本結構是很小的結構體;而字典,管道本身是指針封裝,複製成本都比較低,無需專門做優化。
- 如果range目標表達式是函數調用,也僅被執行一次
package main import "fmt" func data() []int { fmt.Println("original data....") return []int{10,20,30} } func main(){ for i,x := range data(){ fmt.Println(i,x) } }
建議嵌套不要超過兩層,否則會難以維護。必要時可以剝離,重構函數
- break & continue
package main func test1(){ for i :=0;i < 10 ;i++{ if i > 7{ break } println(i) } } func test2(){ outer: //定義標籤,break和continue可在多層嵌套中指定標籤 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,":") } } } func main(){ test1() test2() }