go 基礎

wacho發表於2019-10-22

1、go 程式必須有一個 main 包
2、必須有main 主函式
3、需要匯入一些包,比如 fmt
4、'{' 必須和方法名在同一行
5、匯入的包必須在方法中使用,否則報錯

package main
import "fmt"
func main()  {  // '{' 必須和方法名在同一行
fmt.Println("hello word")
}

可以包含資料的變數(或常量),可以使用不同的資料型別或型別來儲存資料。使用 var 宣告的變數的值會自動初始化為該型別的零值。型別定義了某個變數的值的集合與可對其進行操作的集合。

型別可以是基本型別,如:int、float、bool、string;結構化的(複合的),如:struct、array、slice、map、channel;只描述型別的行為的,如:interface。

結構化的型別沒有真正的值,它使用 nil 作為預設值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是 NULL 或 0)。值得注意的是,Go 語言中不存在型別繼承。
函式也可以是一個確定的型別,就是以函式作為返回型別。這種型別的宣告要寫在函式名和可選的引數列表之後,例如:

func FunctionName (a typea, b typeb) typeFunc

float 型別 以及字元型別

float 型別,自動識別預設為 float64,比float32 更加精確
f:= 3.14
fmt.Printf("f type is %T\n",f)  // f type is float64

// 字元型別 var ch byte 
//ch = 97 
// 格式化輸出,%c 以字元方式列印,%d 以整數形式列印 
//fmt.Printf("%c,%d \n", ch, ch) ch = 'a' // 字元 用單引號
fmt.Printf("%c,%d \n", ch, ch) // 大小寫轉換,大寫 字母比 小寫 字母數字 小 32

1、iota 常量自動生成器,每個一行,自動累加1
2、iota 給常量賦值使用

const (
            a = iota
            b = iota
            c = iota
        )

        fmt.Printf("a = %d, b = %d ,c = %d \n", a, b, c)

        //3、iota 遇到const 重置為 0
        const d = iota
        fmt.Printf("d = %d\n", d) // d = 0

        // 4、可以只寫一個iota
        const (
            a1 = iota // 0
            b1
            c1
        )

        fmt.Printf("a1 = %d,b1 = %d,c1 = %d \n", a1, b1, c1)
        // 5、如果在同一行,值都一樣
        const (
            i = iota
            j1,j2,j3 = iota,iota,iota
            k = iota
        )
        fmt.Printf("i = %d,j1 = %d,j2 = %d,j3 = %d,k = %d\n",i,j1,j2,j3,k)

var t = 22 + 33i\
fmt.Println("t is ", t)

t is  (22+33i)

fmt.Printf("t type is %T \n", t)

t type is complex128 

透過內建函式,取得實部和虛部,
fmt.Println("real(t) = ", real(t), "imag(t) = ", imag(t))

real(t) =  22 imag(t) =  33
var tt int
fmt.Printf("請輸入變數tt :")
// 阻塞等待使用者輸入\
//fmt.Scanf("%d",&tt)  // 別忘了 &\
fmt.Scan(&tt)\
fmt.Println("輸入的變數是tt:", tt)
    type bigint int64
        var a bigint
        fmt.Printf(" a type is %T\n", a) //  a type is main.bigint
        // a type is main.bigint
        type (
            long int64
            char byte
        )
        var b long = 77
        var ch char = 'c'
        fmt.Printf("b = %d,ch = %c\n", b, ch) 
        //b = 77,ch = c
        fmt.Printf(" b type is %T\n", b)
        //b type is main.long
        fmt.Printf(" ch type is %T\n", ch)
        //ch type is main.char

for 初始化條件;判斷條件;條件變化

sum:=0
        for i:=1;i <= 100 ;i ++  {
            sum += i
        }
        fmt.Println("sum = ",sum)
// 透過 for 列印每個字元
        abc := "abc"
        for i := 0; i < len(abc); i++ {
            fmt.Printf("str[%d] = %c\n", i, abc[i])
        }

        // 迭代列印每個元素,預設返回2個值, 一個是元素的位置,一個是元素本身
        abc := "abc"
        for i,data := range abc{
            fmt.Printf("abc[%d] = %c\n",i,data)
        }

        for i := range abc{
            fmt.Printf("str[%d] = %c\n", i, abc[i])
        }

        for i ,_ := range abc{  // 第二個返回值,預設丟棄
            fmt.Printf("str[%d] = %c\n", i, abc[i])
        }

函式分為無參無返回值、有參有返回值、無參有返回值、有參無返回值四種
1、無引數無返回值的函式定義

func Wcc() {
    a := 66
    fmt.Println("a = ", a)
}

2、// 有引數 無返回值函式定義

一個引數
func wcc(a int) {
    fmt.Println("a=", a)
}
二個引數
func wcc1(a int, b int) {\
   fmt.Printf("a = %d,b = %d\n", a, b)\
   fmt.Println("a + b = ", a+b)\
}
不定引數
//  ...int 這樣的型別,叫做不定引數型別\
//  不定引數,只能放在形參的最後一個引數\
func wcc2(args ...int) {
   fmt.Println("len(args) = ", len(args))

   for i := 0; i < len(args); i++ {
      fmt.Printf("args[%d] = %d\n", i, args[i])
   }

   fmt.Println("===========================")
   for i, data := range args {\
      fmt.Printf("args[%d] = %d\n", i, data)\
   }
}

3、無引數有返回值

    // 無引數,有返回值,只有一 個返回值
// 有返回值的函式必須使用 return 返回
func t1() int {
   return 2233
}

    // 給返回值起一個變數名,go推薦寫法
// 推薦寫法\
func t2() (result int)  {
   result = 2233
  return
}

    // 多個返回值
func t3() ( a int,b int,c int) {
   a,b,c = 111,222,333
  return
}

func main() {
   // 無引數,有返回值函式呼叫\
  var a int
  a = t1()
   fmt.Println("a = ", a)

   b := t1()
   fmt.Println("b = ", b)

   c:= t2()
   fmt.Println("c = ",c)

   d,e,f := t3()
   fmt.Printf("d=%d,e=%d,f=%d\n",d,e,f)
}

4 、有引數有返回值

//求和, 實現 1+2 +3 ...... + 100
func test2(i int) (sum int) {
    if i == 1 {
        return 1
    }
    sum = i + test2(i-1)
    return
}

func main() {
   var sum int
   sum = test2(100)
   fmt.Println("sum = ", sum)
}

5、函式回撥
有一個引數是函式型別,這個函式就是回撥函式

package main
import "fmt"

type FuncType func(int, int) int

// 實現加法
func Add(a,b int)  int{
    return  a + b
}

func Mul(a ,b int) int  {
    return  a * b
}

func Calc(a, b int, fTest FuncType) (result int) {
    fmt.Println("Clac")
    result = fTest(a,b)
    return
}

// 把 FuncType 不單獨定義,直接寫成匿名函式
func Calcc(a, b int, fTest func(int, int) (int)) (result int) {
    fmt.Println("Clacc")
    result = fTest(a,b)
    return
}

func main() {
    a := Calc(1,2,Add)
    fmt.Println("a = ",a)
    b := Calcc(1,2,Mul)
    fmt.Println("b = ",b)
}

6、匿名函式和閉包

    a := 10
    str := "visco"

    //匿名函式,沒有名字,函式定義,還沒呼叫
    f1 := func() { // := 自動推導型別
        fmt.Println("a = ", a)
        fmt.Println("str = ", str)
    }

    f1()
    // a =  10   
    //str =  visco

    // 定義匿名函式,同時呼叫
    func() {
        fmt.Printf("a = %d,str = %s\n", a, str)
    }() //後面的 ()表示呼叫匿名函式本身,沒引數就不傳

    //a = 10,str = visco
    // 帶引數的匿名函式
    f2 := func(i, j int) {
        fmt.Printf("i = %d,j = %d\n", i, j)
    }

    f2(2, 3)
    // i = 2,j = 3

    // 帶引數的匿名函式
    func(d, e int) {
        fmt.Printf("d = %d,e = %d\n", d, e)
    }(4, 5)
// d = 4,e = 5

    // 匿名函式,有引數,有返回值
    x, y := func(i, j int) (max, min int) {
        if i > j {
            max = i
            min = j
        } else {
            min = i
            max = j
        }
        return
    }(3, 8)

    fmt.Printf("x = %d,y = %d\n", x, y)
// x = 8,y = 3
    a := 2
    str := "nike"
    func() {
        // 閉包以  '引用'  方式捕獲外部變數,裡面的改了,外面的變數也會改
        a = 2233
        str = "wcc"
        fmt.Printf("內部a = %d,str = %s\n", a, str)
    }() // () 代表直接呼叫
    // a = 2233,str = wcc

    fmt.Printf("外部a = %d,str = %s\n", a, str)
    // a = 2233,str = wcc

因為這裡閉包捕獲變數是以 引用的方式,所以外部變數也會改變

先看普通的一個函式,呼叫後的結果

// 普通函式
func test3() int {
    // 函式被呼叫時,x 才被分配空間,才初始化為 0
    var x int    // 初始化為 0
    x++          // x=1
    return x * x //函式呼叫完畢,x 自動釋放
}

func main() {
    fmt.Println(test3())  // 1
    fmt.Println(test3())// 1
    fmt.Println(test3())// 1
    fmt.Println(test3())// 1
}

發現所有返回值都是 1,因為這裡 x 每次呼叫完畢都會釋放,然後每次都從 0 變成 1.
接下來用閉包函式實現

// 函式的返回值是一個匿名函式,返回一個函式型別
func t4() func() int {

    var x int // 初始化為 0
    return func() int {
        x++         // 第一次 x =1,第二次 x=2,以此類推,每次 x 都沒釋放
        return x * x 
    }
}

    //返回值為一個匿名函式,返回一個函式型別,所以透過變數 f 來呼叫返回的匿名函式,f來呼叫閉包函式
    //他不關心捕獲的變數和常量是否超出了作用域
    //所以只要閉包還在使用,變數就存在
    f := t4()

    fmt.Println(f())// 1
    fmt.Println(f())// 4
    fmt.Println(f())// 9
    fmt.Println(f())// 16

defer 函式,延遲呼叫,main函式 結束前呼叫
多個defer ,會按照 先進後出 的順序執行,最後一個最先執行
無論中間發生什麼錯誤,defer 都會執行
首先看一個簡單的例子

func main() {
    defer fmt.Println("aaaa")

    fmt.Println("bbbb")
}

正常情況下,應該按照順序列印 aaaa bbbb
但是定義了一個 defer 函式,延遲了 aaaa 的呼叫,先呼叫了 bbbb,再列印 aaaa

a := 1
b := 3

    defer func(a, b int) {
        fmt.Printf("a = %d,b = %d\n", a, b)
    }(a, b) // a,b 先獲取到上面的a ,b 的值1,3,只是最後才會去呼叫

    a = 22
    b = 33
    fmt.Printf("a = %d,b = %d\n", a, b)

這個結果列印的是 a=22,b=33。a=1,b=3
這裡先列印最後的 22 和 33,defer 函式會先獲取到上面定義的 a b 變數,先拿到值,但沒呼叫,只是在最後結尾的時候才會觸發,最後列印出來 a=1,b=3

下面看另一種類似呼叫,

    a := 1
    b := 3

    defer func() {
        fmt.Printf("a = %d,b = %d\n", a, b)
    }()
    //a = 22,b = 33

    a = 22
    b = 33
    fmt.Printf("a = %d,b = %d\n", a, b)
    //a = 22,b = 33

這時候列印的 全是 a=22,b=33。以為上面 defer 沒有傳參 a b ,會在最後獲取 a b 的值,因為最後才去呼叫,下面的 a b 值會直接覆蓋上面的值,所以a=22,b=33

下面是多個 defer 的執行順序,會按照先進後出的原則,最後一個最先執行。

fmt.Println(123)
defer fmt.Println("this is defer test")
defer func() {
   fmt.Println(2233)
}()

結果會是
2233
this is defer test
123

package main
import "fmt"
import "os"

func main()  {
    // 接受使用者傳遞的引數,都是以字串方式傳遞
    list:=os.Args
    n:= len(list)
    fmt.Println("長度是:",n)

    for i:=0;i<n ;i++  {
        fmt.Printf("list[%d] = %s\n",i,list[i])
    }

    //利用迭代同樣列印輸入的每個元素
    for i,data:=range list{
        fmt.Printf("list[%d] = %s\n",i,data)
    }
}

我執行 go run 12獲取命令列引數.go sss ddd aaa
以 空格 為單位,12獲取命令列引數.go這個是第一個引數,sss 是第二個引數,ddd 是第三個引數,aaa 是第四個引數
執行結果 為:
長度是: 4
list[0] = /var/folders/5v/m892g1s92wn4zh2yd6_xwkth0000gn/T/go-build845588790/b001/exe/12獲取命令列引數
list[1] = sss
list[2] = ddd
list[3] = aaa

定義在{}裡面的變數就是區域性變數,只能在{}裡面使用,
執行到定義變數的那句話,才開始分配空間,離開作用域自動釋放

package main

import "fmt"

func main() {
    // 定義在{}裡面的變數就是區域性變數,只能在{}裡面使用
    //執行到定義變數的那句話,才開始分配空間,離開作用域自動釋放
    {
        i := 10
        fmt.Println("i=", i) // i=10
    }

    if flag := 4; flag == 4 {
        fmt.Println("flag =  ", flag)
    }

    flag = 5 // undefined: flag

}

執行這段程式碼報錯, go go run 13區域性變數.go

./13區域性變數.go:17:2: undefined: flag
會提醒 flag undefind,flag 在作用域定義了 值,離開{}作用域後自動釋放了。

package main

import "fmt"

var a byte // 全域性變數

func main() {

    var a int // 區域性變數

    //1、不同的 作用域,允許定義同名變數
    //2、使用變數的原則,就近原則

    fmt.Printf("%T\n", a)   // int

    {
        var a float32
        fmt.Printf("2 : %T\n", a)   // float32
    }

    testc()
}

func testc()  {
    fmt.Printf("3 : %T\n", a)   // uint8,也就是 byte,z這裡只能使用全域性的變數,區域性的獲取不到
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章