Go語言學習筆記(二)十分鐘上手

加 Golang學習 QQ羣共同窗習進步成家立業工做 ^-^ 羣號:96933959數組

變量&常量

變量

  變量名由字母、數字、下劃線組成,不能以數字開頭。閉包

...
var (
    A int    //默認爲0
    B string //默認爲""
    C bool   //默認爲false

    Name string = "suoning"
    Age         = 18 // 自動識別類型
)

func main() {
    sex := "man"          //等價於 var sex string = "man"
    var girlfriend string //聲明變量
    girlfriend = "Dawn"   //變量賦值

    Name := "Nick" //正確
    //girlfriend := "Jenny"    //錯誤的!不能申明兩次
    ...
}

 

常量

常量使用const 修飾,表明永遠是隻讀的,不能修改。ide

常量中的數據類型只能夠是布爾型、數字型(整數型、浮點型和複數)和字符串型。函數

語法:const identifier [type] = value,其中type能夠省略。性能

iota

iota,特殊常量,能夠認爲是一個能夠被編譯器修改的常量。學習

在每個const關鍵字出現時,被重置爲0,而後再下一個const出現以前,每出現一次iota,其所表明的數字會自動增長1。ui

...
const (
    A = 'a'    //97
    B = iota   //1
    C          //2
    D = "nick" //"nick"  iota += 1
    E          //"nick"  iota += 1
    F = 618    //618  iota +=1
    G          //618  iota +=1
    H = iota   //7
    I          //8
)
...

 

基本數據類型

布爾型

布爾型的值只能夠是常量 true 或者 false。spa

var a bool
var a bool = true
var a = false

 

數字類型

int:命令行

  • uint8(無符號 8 位整型 (0 到 255))
  • uint16(無符號 16 位整型 (0 到 65535))
  • uint32(無符號 32 位整型 (0 到 4294967295))
  • uint64(無符號 64 位整型 (0 到 18446744073709551615))
  • int8(有符號 8 位整型 (-128 到 127))
  • int16(有符號 16 位整型 (-32768 到 32767))
  • int32(有符號 32 位整型 (-2147483648 到 2147483647))
  • int64(有符號 64 位整型 (-9223372036854775808 到 9223372036854775807))

float:3d

  • float32(IEEE-754 32位浮點型數)
  • float64(IEEE-754 64位浮點型數)
  • complex64(32 位實數和虛數)
  • complex128(64 位實數和虛數)

其它:

  • byte(相似 uint8)
  • rune(相似 int32)
  • uint(32 或 64 位)
  • int(與 uint 同樣大小)
  • uintptr(無符號整型,用於存放一個指針) 

類型轉換

package main

func main() {
    var a int
    var b int32
    var c int64
    a = 15
    //b = a + a // compiler error
    b = int32(a + a) // ok
    b = b + 5 // ok: 5 is a constant
    c = c + 5 // ok
}

 

字符類型

存儲爲ascii碼

var a byte = 'a'
fmt.Println(a)    //97

 

字符串類型

字符串表示兩種方式:

  1. 雙引號 
  2. `` (反引號,不轉義)

string底層就是一個byte的數組

string自己是不可變的,所以要改變string中字符,須要以下操做:

str := "hello world"
s := []byte(str)
s[0] = 'o'
str = string(s)

 

操做符

算術運算符

  • + 相加
  • - 相減
  • * 相乘
  • / 相除
  • % 求餘
  • ++ 自增
  • -- 自減

 

關係運算符

  • == 檢查兩個值是否相等,若是相等返回 True 不然返回 False。
  • != 檢查兩個值是否不相等,若是不相等返回 True 不然返回 False。
  • > 檢查左邊值是否大於右邊值,若是是返回 True 不然返回 False。
  • < 檢查左邊值是否小於右邊值,若是是返回 True 不然返回 False。
  • >= 檢查左邊值是否大於等於右邊值,若是是返回 True 不然返回 False。
  • <= 檢查左邊值是否小於等於右邊值,若是是返回 True 不然返回 False。

 

邏輯運算符

  • && 邏輯 AND 運算符。 若是兩邊的操做數都是 True,則條件 True,不然爲 False。
  • || 邏輯 OR 運算符。 若是兩邊的操做數有一個 True,則條件 True,不然爲 False。
  • ! 邏輯 NOT 運算符。 若是條件爲 True,則邏輯 NOT 條件 False,不然爲 True。

 

位運算符

位運算符對整數在內存中的二進制位進行操做。

  • & 按位與運算符"&"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相與。
  • | 按位或運算符"|"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相或。
  • ^ 按位異或運算符"^"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果爲1。
  • << 左移運算符"<<"是雙目運算符。左移n位就是乘以2的n次方。 其功能把"<<"左邊的運算數的各二進位所有左移若干位,由"<<"右邊的數指定移動的位數,高位丟棄,低位補0。
  • >> 右移運算符">>"是雙目運算符。右移n位就是除以2的n次方。 其功能是把">>"左邊的運算數的各二進位所有右移若干位,">>"右邊的數指定移動的位數。

 

賦值運算符 

  • = 簡單的賦值運算符,將一個表達式的值賦給一個左值
  • += 相加後再賦值 (C += A 等於 C = C + A)
  • -= 相減後再賦值 (C -= A 等於 C = C - A)
  • *= 相乘後再賦值 (C *= A 等於 C = C * A)
  • /= 相除後再賦值 (C /= A 等於 C = C / A)
  • %= 求餘後再賦值 (C %= A 等於 C = C % A)
  • <<= 左移後賦值 (C <<= 2 等於 C = C << 2)
  • >>= 右移後賦值 (C >>= 2 等於 C = C >> 2)
  • &= 按位與後賦值 (C &= 2 等於 C = C & 2)
  • ^= 按位異或後賦值  (C ^= 2 等於 C = C ^ 2)
  • |= 按位或後賦值 (C |= 2 等於 C = C | 2)

 

流程控制

If/else & for & range

注意 else if / else 位置

if condition1 {
} else if condition2 {
} else if condition3 {

} else {
}

for循環條件沒有小括號

for i := 0; i < 10; i++ {
}

死循環

for true {
}
能夠簡寫爲:
for {
}

range

for i, v := range str {
}
package main

import (
    "fmt"
)

func ran(str string) {
    for i, v := range str {
        fmt.Printf("index[%d] val[%c] len[%d]\n", i, v, len([]byte(string(v))))
    }
}

func main() {
    ran("Love, 索寧")
}


輸出結果:
index[0] val[L] len[1]
index[1] val[o] len[1]
index[2] val[v] len[1]
index[3] val[e] len[1]
index[4] val[,] len[1]
index[5] val[ ] len[1]
index[6] val[索] len[3]
index[9] val[寧] len[3]
range栗子

 

switch case

Go裏面switch默認至關於每一個case最後帶有break,匹配成功後不會自動向下執行其餘case,而是跳出整個switch。

switch var {
case var1:
case var2:
case var3:
default:
}
func sw(num int) {
    switch num {
    case 1, 2, 3:
        fmt.Printf("%s in 1,2,3\n", num)
    case 4, 5, 6:
        fmt.Printf("%s in 4,5,6\n", num)
        fallthrough
    case 7, 8, 9:
        fmt.Printf("%s big 789\n", num)
    default:
        fmt.Printf("default...\n")
    }
}
switch 栗子一
func sw2(num int) {
    switch {
    case num > 0 && num < 4:
        fmt.Printf("%s in 1,2,3\n", num)
    case num > 4 && num < 7:
        fmt.Printf("%s in 4,5,6\n", num)
        fallthrough
    case num > 7 && num < 10:
        fmt.Printf("%s big 789\n", num)
    default:
        fmt.Printf("default...\n")
    }
}
switch 栗子二
func sw3() {
    switch num := 5; {
    case num > 0 && num < 4:
        fmt.Printf("%s in 1,2,3\n", num)
    case num > 4 && num < 7:
        fmt.Printf("%s in 4,5,6\n", num)
        fallthrough
    case num > 7 && num < 10:
        fmt.Printf("%s big 789\n", num)
    default:
        fmt.Printf("default...\n")
    }
}
switch 栗子三

fallthrough

可使用fallthrough強制執行後面的case代碼。 

package main

import "fmt"

func main() {

    switch {
    case false:
            fmt.Println("The integer was <= 4")
            fallthrough
    case true:
            fmt.Println("The integer was <= 5")
            fallthrough
    case false:
            fmt.Println("The integer was <= 6")
            fallthrough
    case true:
            fmt.Println("The integer was <= 7")
    case false:
            fmt.Println("The integer was <= 8")
            fallthrough
    default:
            fmt.Println("default case")
    }
}


運行結果:

The integer was <= 5
The integer was <= 6
The integer was <= 7
fallthrough栗子

 

label & goto

label要寫在for循環的開始而不是結束的地方。直接break退出到指定的位置。

func lab() {
LABLE:
    for i := 0; i < 10; i++ {
        for true {
            i++
            if i == 6 {
                break LABLE
            }
            fmt.Println(i)
        }
    }
}

 goto語句能夠跳轉到本函數內的某個標籤

func got() {
    i := 0
HERE:
    fmt.Println(i)
    i++
    if i == 5 {
        return
    }
    goto HERE
}

 

select

select與switch相似,不過select有較多限制。

每一個case語句裏必須是一個channel操做;

select {
case ch <- 0:
    //若是0寫入,則進行該case
case <- ch:
    //若是讀到數據,則進行該case
default:
    //若是上面的都沒有成功,則進入default處理
}
package main

import (
    "fmt"
    "time"
)

/*
隨機向ch中寫入一個0或者1的過程,固然這是個死循環。
*/

func main() {
    ch := make(chan int, 1)
    for {
        select {
        case ch <- 0:
        case ch <- 1:
        }
        i := <-ch
        fmt.Println(i)
        time.Sleep(time.Second)
    }
}
select栗子一
package main

import (
    "fmt"
    "time"
)

/*
channel超時處理
一直沒有從ch中讀取到數據,但從timeout中讀取到了數據
*/

func main() {
    ch := make(chan bool)
    timeout := make(chan bool, 1)

    go func() {
        time.Sleep(time.Second*2)
        timeout <- true
    }()

    select {
    case <- ch:
        fmt.Println("This is ch.")
    case <- timeout:
        fmt.Println("This is timeout.")
    }
}
select栗子二(channel超時處理)

 

函數

Go 語言最少有個 main() 函數。

函數聲明告訴了編譯器函數的名稱,返回類型,和參數。

不支持重載,一個包不能有兩個名字同樣的函數。

func function_name( [parameter list] ) [return_types] {
   函數體
}

 

命名返回值的名字(return能夠不指定變量):

func add(a, b int) (c int) {
        c = a + b
        return

}
栗子一
func calc(a, b int) (sum int, avg int) {
        sum = a + b
        avg = (a +b)/2
        return

}
栗子二

 

_標識符,用來忽略返回值:

func calc(a, b int) (sum int, avg int) {
        sum = a + b
        avg = (a +b)/2
        return
}
func main() {
      sum, _ := calc(100, 200)
}
栗子

 

函數也是一種類型,一個函數能夠賦值給變量

package main

import "fmt"

//申明一個函數類型
type add_func func(int, int) int

func add(a, b int) int {
    return a + b
}

func operator(op add_func, a int, b int) int {
    return op(a, b)
}

func main() {
    c := add
    fmt.Println(c)    //0x1087050
    sum := operator(c, 1, 2)
    fmt.Println(sum)    //300
}

 

可變參數

其中arg是一個slice,咱們能夠經過arg[index]依次訪問全部參數;經過len(arg)來判斷傳遞參數的個數。

0個或多個參數
func add(arg…int) int {
}
1個或多個參數
func add(a int, arg…int) int {
}
2個或多個參數
func add(a int, b int, arg…int) int {
}
package main

import (
    "fmt"
)

//返回值指定爲sum變量,默認會return這個變量
func add(a int, b int, arg ...int) (sum int) {
    sum = a + b
    for i := 0; i < len(arg); i++ {
        sum += arg[i]
    }
    return
}

func concat(s string, arg ...string) string {
    str := s
    for i := 0; i < len(arg); i++ {
        str += arg[i]
    }
    return str
}

func main() {
    sum := add(1, 2, 3, 4, 5, 6, 7)
    fmt.Println(sum)    //28
    str := concat("nick", " ", "and", " ", "dawn", ".")
    fmt.Println(str)    //nick and dawn.
}
多參數栗子

 

main & init & defer

main & init

init() 方法是在任何package中均可以出現;
main() 方法只能用在package main 中。
Go程序會自動調用init()和main(),因此你不須要在任何地方調用這兩個函數。
每一個package中的init函數都是可選的,但package main就必須包含一個main函數。
main()函數不能帶參數,也不能定義返回值。命令行傳入的參數在os.Args變量中保存。若是須要支持命令行開關,可以使用flag包。 
 

defer

  1. 當函數返回時,執行defer語句;
  2. 多個defer語句,按先進後出的方式執行;
  3. defer語句中的變量,在defer聲明時肯定變量
  4. 觸發異常也會走defer語句。
package main

import "fmt"

//聲明defer時,變量i就爲0
func test1()  {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

//棧,先進先出
func test2()  {
    for i := 0; i < 5; i++ {
        defer fmt.Printf("->%d", i)
    }
}

func main() {
    test1()
    test2()
}


輸出:
0
->4->3->2->1->0
defer 栗子
package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印後") }()

    panic("觸發異常")
}

/*
打印後
打印中
打印前
panic: 觸發異常
*/
defer 栗子二
 

做用域

  1. 在函數內部聲明的變量叫作局部變量,生命週期僅限於函數內部。
  2. 在函數外部聲明的變量叫作全局變量,生命週期做用於整個包,若是是大寫的,則做用於整個程序。
package main

import "fmt"

var name string

func main() {
    name = "Nick"
    fmt.Println(name)
    f1()
}
func f1() {
    name := "Dawn"
    fmt.Println(name)
    f2()
}
func f2() {
    fmt.Println(name)
}

輸出:
Nick
Dawn
Nick
做用域栗子

 

匿名函數 & 閉包

匿名函數

匿名函數是由一個不帶函數名的函數聲明和函數體組成。

package main

func main()  {

    f := func(x, y int) int {
        return x + y
    }
    f(1,1)

    ch := make(chan int)
    func (ch chan int)  {
        ch <- 9
    }(ch)
}

 

閉包

閉包是一個函數和與其相關的引用環境組合而成的實體。

函數能夠存儲到變量中做爲參數傳遞給其它函數,可以被函數動態的建立和返回。

func Adder() func(int) int {
    var x int
    return func(d int) int {
        x += d
        return x
    }
}

f := Adder()
fmt.Println(f(1))    //1
fmt.Println(f(10))    //11
fmt.Println(f(100))    //111
package main

import (
    "fmt"
    "strings"
)

func makeSuffix(suffix string) func(string) string {
    return func(name string) string {
        if !strings.HasSuffix(name, suffix) {
            return name + suffix
        }
        return name
    }
}

func main() {
    f1 := makeSuffix(".png")
    fmt.Println(f1("name1"))    //name1.png
    fmt.Println(f1("name2"))    //name2.png

    f2 := makeSuffix(".jpg")
    fmt.Println(f2("name1"))    //name1.jpg
    fmt.Println(f2("name2"))    //name2.jpg
}
閉包栗子二

 

值傳遞 & 引用傳遞

不管是值傳遞,仍是引用傳遞,傳遞給函數的都是變量的副本;

值傳遞是值的拷貝,引用傳遞是地址的拷貝;

通常來講,地址拷貝更爲高效。而值拷貝取決於拷貝的對象大小,對象越大,則性能越低。

map、slice、chan、指針、interface默認以引用的方式傳遞。

new 內置函數 用來分配內存,主要用來分配值類型,好比int、struct,返回的是指針;

make 內置函數 用來分配內存,主要用來分配引用類型,好比chan、map、slice。

 

程序初始化與執行過程

程序的初始化和執行都起始於main包。
若是main包還導入了其它的包,那麼就會在編譯時將它們依次導入。
有時一個包會被多個包同時導入,那麼它只會被導入一次(例如不少包可能都會用到fmt包,但它只會被導入一次,由於沒有必要導入屢次)。
當一個包被導入時,若是該包還導入了其它的包,那麼會先將其它包導入進來,而後再對這些包中的包級常量和變量進行初始化,接着執行init函數(若是有的話),依次類推。
等全部被導入的包都加載完畢了,就會開始對main包中的包級常量和變量進行初始化,而後執行main包中的init函數(若是存在的話),最後執行main函數。
 

 

指針類型(&*)  

普通類型,變量存的就是值,也叫值類型;

指針類型,變量存的是一個地址,這個地址存的纔是值。

變量是一種佔位符,用於引用計算機內存地址;

Go 語言的取地址符是 &,放到一個變量前使用就會返回相應變量的內存地址。

獲取指針類型所指向的值,使用:*。

 

一個指針變量能夠指向任何一個值的內存地址它指向那個值的內存地址。

申明以下:

var age *int           //指向整型
var height *float32    //指向浮點型

當一個指針被定義後沒有分配到任何變量時,它的值爲 nil。

nil 指針也稱爲空指針。

 

栗子

package main

import "fmt"

func main() {
    var ptr *int
    num := 100
    ptr = &num
    fmt.Println(ptr)    //0xc42000e1f8
    fmt.Println(*ptr)   //100
    *ptr = 200
    fmt.Println(num)    //200
}
package main

import "fmt"

func change(num *int) {
    fmt.Println(num)    //0xc42000e1f8
    fmt.Println(*num)   //100
    *num = 1000
    fmt.Println(num)    //0xc42000e1f8
    fmt.Println(*num)   //1000
}

func main() {
    num := 100
    fmt.Println(&num)    //0xc42000e1f8
    change(&num)
    fmt.Println(&num)    //0xc42000e1f8
    fmt.Println(num)     //1000
}
相關文章
相關標籤/搜索