GO基礎知識分享

小魔童哪吒發表於2021-03-31
[TOC]

兵長:喲,最近在幹啥呢

胖sir:在看我之前的go基礎學習資料呢,回顧一下

兵長:那給我分享一下唄,我也想回顧回顧

胖sir:用你的小手指點開你的手機,我來傳給你

兵長:你信不信我的小手指可以帶你飛整個峽谷 . . .

go語言的基本事項

  1. go run hello.go 直接執行,輸出結果(原理也是編譯後執行)
  2. go build hello.go 生成可執行程式,執行可執行程式,輸出結果
  3. 注意 go語言中花括號不能單獨佔一行,否則會報錯
package main

import "fmt"

func main(){ //go語言中此處的花括號不能單獨佔一行,否則會報錯
    fmt.Println("hello world")
}
  1. go語言一條語句佔一行,如果一行需要執行多個語句 使用 分號 隔開

  2. go語言的輸出語句有3種方式

    1. import “fmt” 後適用fmt.Println(x) – 輸出
    2. println(x) – 輸出
    3. fmt.Printf(“%d”,x) – 格式化輸出

關鍵字

下面列舉了 Go 程式碼中會使用到的 25 個關鍵字或保留字:

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

除了以上介紹的這些關鍵字,Go 語言還有 36 個預定義識別符號:

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

字串的拼接和變數的定義方式

定義變數的三種方式

  1. 正常使用var定義變數
  2. 使用var定義變數,但是不定義型別,通過賦初值的方式,go編譯器自動識別
  3. 使用:=的方式來進行 新變數的定義,僅限於新變數 – 適用於定義在函式內部
//字串 可以使用+ 進行拼接
    fmt.Println("this is my func")
    fmt.Println("hello ,wolrd" + "xiaozhuzhu")

//定義變數
    var  name string="xiaomotong"
    var age,tail int=24,170
    fmt.Println(name, age , tail)
    fmt.Println(name)
    fmt.Println(age)
    fmt.Println(tail)

//定義變數的三種方式
//1
    var a int = 1
    fmt.Println(a)

//2 使用var定義變數,但是不定義型別,通過賦初值的方式,go編譯器自動識別
    var b = "hello"
    fmt.Println(b)

//3 使用:=的方式來進行 新變數的定義,僅限於新變數 
//:= 左側如果沒有宣告新的變數,就產生編譯錯誤
    c := 20
    fmt.Println(c)
    //c:=30 //報錯,因為c已經不是新變數的
    c=30    //正確,是一個正常的賦值操作
    fmt.Println(c)

    c,d:=40,90 //這樣是合法的
    fmt.Println(c,d)

因式分解的方式,僅僅適用於定義全域性變數

//因式分解的方式,僅僅適用於定義全域性變數
var
(
    g_a int = 1
    g_b,g_c int=1,2
)

空白符

空白識別符號 _ 也被用於拋棄值,如值 5 在:_, b = 5, 7 中被拋棄。

_ 實際上是一個只寫變數,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被宣告的變數,但有時你並不需要使用從一個函式得到的所有返回值。

//空白符
    _,e := 2,3
    fmt.Println(e)

const常量

  • 定義const常量
//定義const常量
    const width,height = 10,5
    var area int=width*height
    fmt.Println("面積為", area)  //50
  • const常量用作列舉
const(
    unknow = 0
    man = 1
    woman =    2
)

println(unknow,man,woman)  //0 1 2
  • 常量可以用len(), cap(), unsafe.Sizeof()函式計算表示式的值。常量表示式中,函式必須是內建函式,否則編譯不過:
const(
    a = "hello"
    b = len(a)
    c = unsafe.Sizeof(a)
)
println(a,b,c)  //hello 5 16

iota的用法

iota,特殊常量,可以認為是一個可以被編譯器修改的常量。

iota 在 const關鍵字出現時將被重置為 0(const 內部的第一行之前),const 中每新增一行常量宣告將使 iota 計數一次(iota 可理解為 const 語句塊中的行索引)。

iota 可以被用作列舉值:

//itoa的用法
const(
    g_a = iota
    g_b
    g_c
    g_d
)

const(
    g_e = iota
    g_f = "hello"
    g_g
    g_h = iota
    g_i
)

const(
    g_j = 1<<iota
    g_k
    g_l
    g_m
)
println(g_a,g_b,g_c,g_d)
println(g_e,g_f,g_g,g_h,g_i)
println(g_j,g_k,g_l,g_m)
//0 1 2 3
//0 hello hello 3 4
//1 2 4 8

運算子

go語言的運算子和C語言的運算子基本一致

Go 沒有三目運算子,不能適用?:

算術運算子

關係運算子

邏輯運算子

位運算子

賦值運算子

其他運算子

語言條件語句

  • if xxx
if xxx {
    ...
}
  • if xxx {…} else{…}
if xxx{
    ...
}else{
    ...
}
  • if xxx{ … if xxx { …}}
if xxx{
    if xxx {
        ...
    }
    ...
}
  • switch
package main

import "fmt"

func main(){

    grade:= 90
    if grade >= 90{
        println("優秀")
    }else if grade >=70 && grade <90{
        println("良好")
    }else{
        println("差")
    }

    var x interface{} //計算型別

    switch i := x.(type){
        case nil:
        fmt.Printf(" x 的型別 :%T\n",i)
        case int:
        fmt.Printf("x 是 int 型")
        default:
        println("未知")
    }
}
  • select

    類似於C語言中的select,用於多路IO複用

for迴圈的方式

  • 三種方式
  1. 類似C語言中的for
  2. 類似C語言中的while
  3. 死迴圈
package main

import "fmt"

func main(){

//類似C語言中的for
    var sum int
    for i:=1;i<=10;i++{
        sum +=i
    }

    fmt.Println(sum)


//類似於while
    for sum >30{
        sum -= 10
        fmt.Println(sum)
    }

//死迴圈

for {
    ...
}
  • For-each range 迴圈
//for-each  range 迴圈的方式
    name := []string{"qqq","yyy"}
    for i,str:= range name{
    fmt.Printf("%d -- %s\n",i,str)
    }

//0 -- qqq
//1 -- yyy
------------------------------------------------------------------------
    str := []string{"北京", "天津", "山東"}

    //可以預設丟掉第二個返回值
    for i := range str {
        fmt.Printf("%d -- %s\n", i, str[i])
    }

函式

go語言的函式,可以有多個返回值,其餘和C語言沒有什麼區別

作用域

與C語言一致

  • 區域性變數
  • 全域性變數
  • 函式形參

陣列&切片

思想和C語言一致,陣列是固定長度的

切片是動態擴容的,類似於C++的vector

切片寫法如下:

name := []string{"xiaomotong","pangsir"}

nums :=[]int{1,2,3,4,5,6}

指標

var ptr1 *int

二級指標

var  a int
var ptr *int
var pptr **int

ptr = &a
pptr = &ptr

指標陣列

var ptr [5]*int

結構體

go語言中的結構體變數,和結構體指標,訪問結構體成員的時候,都是使用 點(.)來進行訪問,如下:

//定義一個結構體
type info struct{
    name string
    age int
    height int
}

//使用
var stu info
stu.name = "xiaomotong"
stu.age = 24
stu.height = 170

fmt.Println(stu.name,stu.age,stu.height)

var stu2 *info = &stu
stu2.name = "pangsir"
stu2.age = 24
stu2.height = 160

fmt.Println(stu2.name,stu2.age,stu2.height)

切片slice

Go 語言切片是對陣列的抽象。

Go 陣列的長度不可改變,在特定場景中這樣的集合就不太適用,Go中提供了一種靈活,功能強悍的內建型別切片(“動態陣列”),與陣列相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。

  • 使用var定義
  • 定義空slice
  • 使用:=定義
  • 使用make來定義 make([]type,len,cap)
  • apend 和 copy的使用
package main

/*
    author:xiaomotong
    file:slice
    function:study slice for golang
*/
import "fmt"


func main(){
//定義切片的方式
//1、使用var定義
    var s1 = []int{1,2,3};
    printInfo(s1);
//2、定義空slice
    var s2 []int
    printInfo(s2);
//3、使用:=定義
    ss := []int{3,4,5,6}
    printInfo(ss);

//4、使用make來定義 make([]type,len,cap)
    s3 := make([]int,2,3)
    printInfo(s3);
//複製操作
    s3[0] = 3
    printInfo(s3);
//覆蓋整個slice
    s1 = s3
    printInfo(s1);

//apend 和 copy的使用
    s3 = append(s3,6,7,8,9)
    printInfo(s3);
//擴容
    s4 := make([]int,len(s3),cap(s3) * 3)
    copy(s4,s3)
    printInfo(s4);
//s[2:]
    println(s4[1:])
    println(s4[:4])
    println(s4[1:3])
    fmt.Printf("s4[1:] = %v \n",s4[1:])
    fmt.Printf("s4[:4] = %v \n",s4[:4])
    fmt.Printf("s4[1:3] = %v \n",s4[1:3])
}

func printInfo(s[]int){
    fmt.Printf("len = %d, cap = %d, slic = %v\n",len(s),cap(s),s);

}

範圍Range

Go 語言中 range 關鍵字用於 for 迴圈中迭代陣列(array)、切片(slice)、通道(channel)或集合(map)的元素。

在陣列和切片中它返回元素的索引和索引對應的值在集合中返回 key-value 對

  • range 對於 陣列、切片
  • 對於字串
  • range對於map集合
  • 佔位符_
package main

/*
    author:xiaomotong
    file:range
    function:study range for golang
*/
import "fmt"


func main(){

//1、range 對於 陣列、切片

    s := []string{"apple","pen"}
    for i,value := range s{
        fmt.Println(i,value)
    }

//2、對於字串
    for i,value := range "hello"{
        fmt.Println(i,value)
    }

//3、range對於map集合
    m := map[string]string{"name":"xiaopang","age":"25"}
    for i,value := range m{
        fmt.Println(i,value)
    }

//4、佔位符_
    sum := 0
    nums := []int{1,2,3,4,5}
    for _,value := range nums{
        sum += value
    }
    fmt.Println(sum)
}

MAP集合

Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索資料,key 類似於索引,指向資料的值。

Map 是一種集合,所以我們可以像迭代陣列和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的。

//類似於key-value的形式
map[string]string

m := map[string]string{"name":"xiaozhu","age":"15"}

mm := make(map[string]string)

countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "羅馬"
countryCapitalMap [ "Japan" ] = "東京"
countryCapitalMap [ "India " ] = "新德里"

delete() 函式

delete() 函式用於刪除集合的元素, 引數為 map 和其對應的 key

delete(countryCapitalMap,"France")

遞迴函式

Go 語言支援遞迴。但我們在使用遞迴時,開發者需要設定退出條件,否則遞迴將陷入無限迴圈中。

遞迴函式對於解決數學上的問題是非常有用的,就像計算階乘,生成斐波那契數列等。

遞迴算階乘

package main

import "fmt"

func fabulaxiaomotong(n uint 64) (result uint64){
    if n>0 {
        return fabulaxiaomotong(n-1)*n
    }
    return 1
}

func main(){
    fmt.Println("result : ",fabulaxiaomotong(15))
}

菲波拉契數列

func fabolaxiaomotong(n uint64)(result utin64){
    if n<2{
        return n
    }else{
        return fabolaxiaomotong(n-2)+fabolaxiaomotong(n-1)
    }
}

介面

Go 語言提供了另外一種資料型別即介面,它把所有的具有共性的方法定義在一起,任何其他型別只要實現了這些方法就是實現了這個介面

package main

import "fmt"

//介面
type phone interface {
    call()
    show()
}

type xiaomi struct {
    name string
    ads  string
}

type huawei struct {
    name string
    ads  string
}

//介面實現
func (x xiaomi) call() {
    fmt.Println("phoneName :", x.name)
}

func (x xiaomi) show() {
    fmt.Println("advertisement :", x.ads)
}

func (h huawei) call() {
    fmt.Println("phoneName :", h.name)
}

func (h huawei) show() {
    fmt.Println("advertisement :", h.ads)
}

func main() {
    x := xiaomi{"mi note2", "for fire"}
    x.call()
    x.show()

    h := huawei{"hw p40", "your better phone"}
    h.call()
    h.show()
}

錯誤

Go 語言通過內建的錯誤介面提供了非常簡單的錯誤處理機制。error型別是一個介面型別,這是它的定義:

package main

import "fmt"

//定義資料結構
type DivideError struct {
    devidee int
    devider int
}

//錯誤處理實現Error()介面
func (de *DivideError) Error() string {
    strdata := `
        error,divide is zero
        dividee is %d
        divider is zero
    `

    return fmt.Sprintf(strdata, de.devidee)
}

//實現功能介面
func Divide(dividee int, divider int) (result int, errMsg string) {
    if divider == 0 {
        data := DivideError{dividee, divider}
        errMsg = data.Error()
        return
    } else {
        return dividee / divider, ""
    }
}

func main() {
    a := 10
    b := 0
    result, err := Divide(a, b)
    if err != "" {
        fmt.Println(err)
        return
    }
    fmt.Printf("%d / %d == %d \n", a, b, result)

}

go語言的併發

Go 語言支援併發,我們只需要通過 go 關鍵字來開啟 goroutine 即可。goroutine 是輕量級執行緒,goroutine 的排程是由 Golang 執行時進行管理的。goroutine 語法格式:

  • go的併發也是執行緒不安全的,需要加鎖才安全
package main

import (
    "fmt"
    "time"
)

func say(s string) {
    var i int
    for i = 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

var num int = 0

//goroutine 是執行緒不安全的
func countNum() {
    var i int
    for i = 0; i < 10; i++ {
        time.Sleep(5 * time.Millisecond)
        num++
    }
}
func main() {
    //go say("hello")
    //say("world")

    go countNum()
    countNum()
    fmt.Println(num)
}

通道(channel)

  • 通道(channel)是用來傳遞資料的一個資料結構。通道可用於兩個 goroutine 之間通過傳遞一個指定型別的值來同步執行和通訊。操作符 <- 用於指定通道的方向,傳送或接收。如果未指定方向,則為雙向通道。

    • 注意:預設情況下,通道是不帶緩衝區的。傳送端傳送資料,同時必須有接收端相應的接收資料。以下例項通過兩個 goroutine 來計算數字之和,在 goroutine 完成計算後,它會計算兩個結果的和:
  • 通道可以設定緩衝區,通過 make 的第二個引數指定緩衝區大小

  • Go 通過 range 關鍵字來實現遍歷讀取到的資料,類似於與陣列或切片

package main

import "fmt"

//不帶緩衝的 通道
func getSum(s []int, c chan int) {
    sum := 0
    for _, value := range s {
        sum += value
    }
    c <- sum
}

func getSum2(c chan int, n int) {
    x, y := 0, 1
    var i int
    for i = 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c) //關閉通道
}

func main() {
    //不帶緩衝的 通道
    // s := []int{3, 5, -2, 3, 4, 7, 1, 1, 1}
    // c := make(chan int)
    // go getSum(s[:3], c)
    // go getSum(s[3:6], c)
    // go getSum(s[6:], c)
    // x, y, z := <-c, <-c, <-c
    // fmt.Println(x, y, z, x+y+z)

//帶緩衝的通道
    c := make(chan int, 10)
    go getSum2(c, cap(c))

    for value := range c {
        fmt.Println(value)
    }

}

自己呼叫別的包/自己的包

自己呼叫別人的包或者自己的包,如上目錄結構

  • 自己寫的包名,要和目錄名一樣
  • 使用go mod 模組 ,執行 go mod init mystudy

mylib.go

package mylib

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

main.go

package main

import (
    "fmt"
    "mystudy/mylib"
)

func main() {
    fmt.Println(mylib.Add(2, 3))
}

以上為本期全部內容,如有疑問可以在評論區或後臺提出你的疑問,我們一起交流,一起成長。

好傢伙要是文章對你還有點作用的話,請幫忙點個關注,分享到你的朋友圈,分享技術,分享快樂

技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。

作者:小魔童哪吒

本作品採用《CC 協議》,轉載必須註明作者和本文連結