go語言基礎知識分享

go語言基礎知識

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="qingbing"
    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{"qingbing","yongchao"}

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 = "qingbing"
stu.age = 24
stu.height = 170

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

var stu2 *info = &stu
stu2.name = "yongchao"
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:qingbing
    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:qingbing
    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 fabulaqi(n uint 64) (result uint64){
    if n>0 {
        return fabulaqi(n-1)*n
    }
    return 1
}

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

菲波拉契數列

func fabolaqi(n uint64)(result utin64){
    if n<2{
        return n
    }else{
        return fabolaqi(n-2)+fabolaqi(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)
    }

}

自己調用別的包/自己的包

image

自己調用別人的包或者自己的包,如上目錄結構

  • 自己寫的包名,要和目錄名一樣
  • 使用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))
}

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

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

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

作者:小魔童哪吒

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 注:本文是《Go語言核心編程》(李文塔/著)個人讀書筆記 Go詞法單元 詞法分析編譯器編譯程序的第一步就是將源程序...
    _羊羽_閱讀 800評論 0 1
  • 第2章 順序編程 2.1 變量 變量聲明 變量聲明的類型信息放在變量之后,放在數組的中括號之后,作返回值類型放在方...
    fjxCode閱讀 442評論 0 1
  • 一定要努力堵住那悠悠眾口 Go的目錄結構:bin存放編譯后的可執行文件;pkg存放編譯后的包文件;src存放項目源...
    一個無趣的人W閱讀 367評論 0 0
  • go語言語法 直接放圖 Go 程序是通過 package 來組織的。一個文件夾既是一個包只有 package 名稱...
    繁星沉黎閱讀 1,480評論 0 2
  • 所有go源碼以.go結尾 標識符以字母或下劃線開頭,大小寫敏感,(_是特殊標識符,還可用來忽略結果)Go語言中的函...
    東方舵手閱讀 457評論 0 0