主頁 > 後端開發 > Golang學習系列第三天:學習陣列、切片、Map、結構體、指標、函式、介面型別、channel通道

Golang學習系列第三天:學習陣列、切片、Map、結構體、指標、函式、介面型別、channel通道

2020-09-10 23:01:21 後端開發

 

       繼Golang學習系列第二天:變數、常量、資料型別和流程陳述句之后,今天開始學習資料型別之高級型別: 派生型別,

學過java的人都知道,java其實就8種基本型別:byte、short、int、long、float、double、char、boolean,但它有參考資料型別:字串、陣列、集合、類、介面等,

而golang也有這樣的劃分,基本型別(Golang學習系列第二天已學過)和派生型別(不叫參考型別),派生型別有以下幾種:陣列型別、切片型別、Map型別、結構體型別(struct)、指標型別(Pointer)、函式型別、介面型別(interface)、Channel 型別,

 

1.  陣列型別

陣列是具有相同資料型別的元素序列, 陣列在宣告中定義了固定的長度,因此不能擴展超過該長度, 陣列宣告為

var variable_name [SIZE] variable_type

讓我們以代碼舉例如下

package main

import "fmt"

func main() {
    var city [5]string
    city[0] = "北京"
    city[1] = "上海"
    city[2] = "廣州"
    city[3] = "深圳"
    city[4] = "濮陽"
    fmt.Println(city[0], city[1], city[2], city[3], city[4])
    fmt.Println(city)
}

將變數city宣告為5個字串的陣列,執行輸出

?

不過也可以在宣告陣列時設定陣列條目,看簡潔版

package main

import "fmt"

func main() {
    var city = [5]string{ "北京", "上海","廣州","深圳","濮陽"}
    fmt.Println(city[0], city[1], city[2], city[3], city[4])
    fmt.Println(city)
    
    //簡寫版
    othercity := [5]string{ "北京", "上海","廣州", "深圳","濮陽"}
    fmt.Printf("%q", othercity)
}

輸出結果?

甚至,當您傳遞值時,可以使用省略號來使用隱式長度

package main

import "fmt"

func main() {
    var city = [5]string{ "北京", "上海","廣州","深圳","濮陽"}
    fmt.Println(city[0], city[1], city[2], city[3], city[4])
    fmt.Println(city)
    
    //簡寫版
    othercity := [5]string{ "北京", "上海","廣州", "深圳","濮陽"}
    fmt.Printf("%q\n", othercity)
    
     //隱士長度
    other_city := [...]string{ "北京", "上海","廣州", "深圳","濮陽"}
    fmt.Printf("%q", other_city)
}

輸出結果?

 以不同方式列印陣列

注意我們使用帶Printf的fmt包以及如何使用%q“動詞”來列印每個參考的元素,

如果我們使用Println或%s動詞,我們將得到不同的結果

package main

import "fmt"

func main() {  
     //不同方式列印陣列
    other_city := [...]string{ "北京", "上海","廣州", "深圳","濮陽"}
    fmt.Println("不同方式列印陣列")
    fmt.Println(other_city)
    fmt.Printf("%s\n", other_city)
    fmt.Printf("%q", other_city)
}

執行后輸出如圖?

 

多維陣列

您還可以創建多維陣列,示例代碼:

package main

import "fmt"

func main() {
    var multi [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            multi[i][j] = i + j
        }
    }
    fmt.Println("二維陣列: ", multi)
}

輸出結果?

 

2.  切片Slices型別

切片包裝陣列可為資料序列提供更通用,更強大和更方便的介面, 除了具有明確維數的項(例如轉換矩陣)外,Go中的大多數陣列編程都是使用切片而不是簡單陣列完成的,

切片包含對基礎陣列的參考,如果您將一個切片分配給另一個,則兩個切片都參考同一陣列, 如果函式采用slice引數,則對slice的元素所做的更改將對呼叫者可見,這類似于將指標傳遞給基礎陣列,

切片指向值陣列,并且還包含長度, 切片可以調整大小,因為它們只是另一個資料結構之上的包裝,

例如, []T is a slice with elements of type T 表示[] T是具有T型別元素的切片,

舉個小例子

package main

import "fmt"

func main() {
    var city = []string{ "北京", "上海","廣州","深圳","濮陽"}
    fmt.Println(city[0], city[1], city[2], city[3], city[4])
    fmt.Println(city)
}

2.1  切分切片

可以對切片進行切片,以創建一個指向相同陣列的新切片值,運算式為

slice[start:end]

表示計算從start到end-1(含)的元素的一部分,

注意:start和end是表示索引的整數,

下面舉個小demo

package main

import "fmt"

func main() {
    var city = []string{ "北京", "上海","廣州","深圳","濮陽","鄭洲"}
    
    fmt.Println(city)
	fmt.Println(city[1:4])

	// missing low index implies 0
	fmt.Println(city[:3])
	// [2 3 5]

	// missing high index implies len(s)
	fmt.Println(city[4:])
}

輸出結果?

2.2   制造切片

除了通過立即傳遞值(切片文字)來創建切片之外,還可以使用make關鍵字創建切片, 您創建一個特定長度的空切片,然后填充每個條目,

package main

import "fmt"

func main() {

	fmt.Println("準備用make方式創建切片")
	cities := make([]string, 3)
	cities[0] = "鄭洲"
	cities[1] = "濮陽"
	cities[2] = "安陽"
	fmt.Printf("%q", cities)
}

輸出結果:?

 它通過分配清零的陣列并回傳參考該陣列的切片來作業,

 

2.3  附加到切片

可以通過append方式附加值到切片中

package main

import "fmt"

func main() {
	//附加到切片
	other_cities := []string{}
	other_cities = append(other_cities, "濮陽")
	fmt.Println(other_cities)
}

看輸出結果?

 

也可以同時附加多個值到切片中,示例代碼同時包括兩個城市鄭洲和濮陽

package main

import "fmt"

func main() {
  
	//附加多值到切片
	other_cities := []string{}
	
	other_cities = append(other_cities, "鄭洲","濮陽")
	
	fmt.Println(other_cities)
}

輸出結果?

 

甚至您還可以使用省略號將切片附加到另一個切片

package main

import "fmt"

func main() {
	fmt.Println("準備用make方式創建切片")
	cities := make([]string, 3)
	cities[0] = "鄭洲"
	cities[1] = "濮陽"
	cities[2] = "安陽"
	fmt.Printf("%q\n", cities)
	
	//附加切片到切片
	other_cities := []string{"南京"}
	
	other_cities = append(other_cities,  cities...)
	
	fmt.Println(other_cities)
}

輸出結果?

注意: 省略號是該語言的內置功能,這意味著該元素是一個集合, 我們無法將字串型別([] string)型別的元素附加到字串切片中,只能附加字串, 但是,在切片后使用省略號(...),表示要附加切片的每個元素, 因為我們要從另一個片段追加字串,所以編譯器將接受操作,因為型別是匹配的,

您顯然無法將[] int型別的切片附加到[] string型別的另一個切片中,

 

2.4  復制切片

切片也可以復制, 在這里,我們創建一個與other_cities長度相同的空切片copycities,并從other_cities復制到copycities中,

package main

import "fmt"

func main() {
   
	fmt.Println("準備用make方式創建切片")
	cities := make([]string, 3)
	cities[0] = "鄭洲"
	cities[1] = "濮陽"
	cities[2] = "安陽"
	fmt.Printf("%q\n", cities)
	
	//附加多值到切片
	other_cities := []string{"南京"}
	
	other_cities = append(other_cities,  cities...)
	
	fmt.Println(other_cities)
	
	//拷貝切片
    fmt.Println("準備用copy方式創建切片")
    copycities := make([]string, len(other_cities))
    copy(copycities, other_cities)
    fmt.Println("copycities:", copycities)
}

輸出結果 ?

2.5  切片長度

您可以隨時使用len檢查切片的長度,

package main

import "fmt"

func main() {
    fmt.Println("準備用make方式創建切片")
	cities := make([]string, 3)
	cities[0] = "鄭洲"
	cities[1] = "濮陽"
	cities[2] = "安陽"
	fmt.Printf("%q\n", cities)
	
	//附加多值到切片
	other_cities := []string{"南京"}
	
	other_cities = append(other_cities,  cities...)
	
	fmt.Println(other_cities)
	
	//列印切片長度
	fmt.Println(len(cities))
	countries := make([]string, 42)
	fmt.Println(len(countries))
}

輸出結果?

2.6  零片

切片的零值為nil, 無切片的長度和容量為0,

package main

import "fmt"

func main() {
	//零片
	var temp []int
    fmt.Println(temp, len(temp), cap(temp))
    if temp == nil {
        fmt.Println("nil!")
    }
}

 輸出結果?

 

3. map型別

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

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

package main

import "fmt"

func main() {
	users := map[string]int{
		"admin":       0,
		"donguangming":       1,
		"student":           2,
	}

	fmt.Printf("%#v", users)
}

輸出結果?

 

當不使用上述map時,使用前必須使用make(不是新的)創建map, nil映射為空,無法分配給它,

package main

import "fmt"

type User struct {
	name string
	age int
	city string
}

var user map[string]User

func main() {

    //通過make創建
	user = make(map[string]User)

	user["dgm"] = User{"董廣明", 99, "南京"}
	fmt.Println(user["dgm"])
}

輸出結果?

 

3.1  操作map

 3.1.1  在map中插入或更新元素

m[key] = elem

  3.1.2    查詢一個元素

elem = m[key]

  3.1.3  洗掉一個元素

delete(m, key)

   3.1.4  測驗鍵是否存在并且有值

elem, ok = m[key]

如果鍵在m中,則確定為true, 如果不是,則ok為false,elem為map元素型別的零值, 同樣,從map中讀取時(如果沒有按鍵)則結果是map元素型別的零值,

綜上合起來代碼如下

package main

import "fmt"

type User struct {
	name string
	age int
	city string
}

var user map[string]User

func main() {
	user = make(map[string]User)
	//賦值
	user["dgm"] = User{"董廣明", 99, "南京"}
	fmt.Println("user:", user)
	//查詢
	fmt.Println(user["dgm"])

    //洗掉
    delete(user, "dgm")
    fmt.Println("此時user:", user)

    //測驗鍵
    _, u := user["dgm"]
    fmt.Println("u:", u)

	var users = map[string]User{
	"dmg": {"董廣明", 99, "南京"},
	"dongguangming": {"董廣明", 88, "南京"},
    }
    
	fmt.Println(users)
}

輸出結果?

 

3.2   map回圈

如何在map上進行迭代?您可以使用回圈范圍來迭代map, 因為映射是無序集合,所以此回圈的值可能會有所不同,

package main

import "fmt"

type User struct {
	name string
	age int
	city string
}

var user map[string]User

func main() {
	
	var users = map[string]User{
	"dmg": {"董廣明", 99, "南京"},
	"dongguangming": {"董廣明", 88, "南京"},
    }
    
	fmt.Println(users)
	
	/*使用鍵輸出map鍵值 */
    for username := range users {
        fmt.Println(username, "用戶=", users [username])
    }
}

輸出結果?

 

4.  指標型別(Pointer)

學過C(上學時第一門編程語言就是C)的人都知道, 指標是存放值記憶體地址的地方, 指標由*定義,根據資料型別定義指標,go也是這么玩的,宣告格式如下

var var_name *var-type

var-type 為指標型別,var_name 為指標變數名,* 號用于指定變數是作為一個指標,

&運算子可用于獲取變數的地址,比如

var ap *int

a := 12
ap = &a

而指標指向的值可以使用*運算子進行訪問,示例如下

package main

import "fmt"

func main() {
   var age int= 99   /* 宣告實際變數 */
   var ip *int        /* 宣告指標變數 */

   ip = &age  /* 指標變數的存盤地址 */

   fmt.Printf("age 變數的地址是: %x\n", &age  )

   /* 指標變數的存盤地址 */
   fmt.Printf("ip 變數儲存的指標地址: %x\n", ip )

   /* 使用指標訪問值 */
   fmt.Printf("*ip指標變數的值: %d\n", *ip )
}

執行以上代碼,輸出結果 ?

 

4.1   空指標

當一個指標被定義后沒有分配到任何變數時,它的值為 nil,nil 指標也稱為空指標,

nil在概念上和其它編程語言的null、None、nil、NULL一樣,都指代零值或空值,

一個指標變數通常縮寫為 ptr,空指標判斷方式:

if(ptr != nil)     /* ptr 不是空指標 */
if(ptr == nil)    /* ptr 是空指標 */

綜上示例代碼如下

package main

import "fmt"

func main() {
   var age int =99 /* 宣告實際變數 */
   var ptr *int        /* 宣告指標變數 */
   var other_ptr *int        /* 宣告指標變數 */
   
   ptr = &age  /* 指標變數的存盤地址 */
   fmt.Printf("age 變數的地址是: %x\n", &age  )

   /* 使用指標訪問值 */
   fmt.Printf("*ptr指標變數的值: %d\n", *ptr )
   
   if(ptr != nil)  {
      /* ptr 不是空指標 */  
      fmt.Printf("ptr指標變數儲存的指標地址: %x\n", ptr )
   }  
   if(other_ptr == nil)  {
      /* other_ptr 是空指標 */
      fmt.Printf("other_ptr指標變數儲存的指標地址: %x\n", other_ptr )
   }  
}

輸出結果?

 

4.2  指標陣列

 指標陣列:簡單點說它是一個陣列,陣列里面的每個元素都是指標,陣列占多少個位元組由陣列本身決定,它是“儲存指標的陣列”的簡稱,格式如下

var ptr [MAX]* type;

ptr 為type指標陣列,因此每個元素都指向了一個值,只是它的值是指標,

package main

import "fmt"

const MAX int = 5

func main() {
  var city = [MAX]string{ "北京", "上海","廣州","深圳","濮陽"}
   var i int
   var othercity [MAX]*string;

   for  i = 0; i < MAX; i++ {
      othercity[i] = &city[i] /* 字串地址賦值給指標陣列 */
   }

   for  i = 0; i < MAX; i++ {
      fmt.Printf("指標陣列:索引:%d 值:%s 值的記憶體地址:%d\n", i, *othercity[i] , othercity[i] )
   }
}

輸出結果?

 

4.3  指標的指標

如果一個指標變數存放的又是另一個指標變數的地址,則稱這個指標變數為指向指標的指標變數,

當定義一個指向指標的指標變數時,第一個指標存放第二個指標的地址,第二個指標存放變數的地址,

指向指標的指標變數宣告格式如下:

var ptr **type;

表示指向指標的指標變數為type,訪問指向指標的指標變數值需要使用兩個 * 號,

package main

import "fmt"

func main() {
  
   var age int
   var ptr *int
   var pptr **int

   age = 99

   /* 指標 ptr 地址 */
   ptr = &age
   /* 指向指標 ptr 地址 */
   pptr = &ptr

   /* 獲取 pptr 的值 */
   fmt.Printf("變數 age = %d\n", age )
   fmt.Printf("指標變數 *ptr = %d,記憶體地址是:%d\n", *ptr, ptr )
   fmt.Printf("指向指標的指標變數 **pptr = %d,記憶體地址是:%d\n", *pptr, pptr)
}

輸出結果?

 

4.4  通過new函式創建指標

go支持通過new函式創建指標, new函式將型別作為引數,并回傳指向新分配的作為引數傳遞的型別的零值的指標,

package main

import "fmt"

func main() {
   //通過new函式創建指標
    size := new(int)
    fmt.Printf("size的默認值= %d, 型別是: %T, 地址是: %v\n", *size, size, size)
    *size = 99
    fmt.Println("更改后新的值是:", *size)
    
}

 輸出結果?

 

4.5  指標引數

可以將指標傳遞給函式

package main

import "fmt"

func changeValue(val *int) {  
    *val = 66
}

func main() {
   var age int
   var ptr *int
   var pptr **int

   age = 99

   /* 指標 ptr 地址 */
   ptr = &age
   /* 指向指標 ptr 地址 */
   pptr = &ptr

   /* 獲取 pptr 的值 */
   fmt.Printf("變數 age = %d\n", age )
   fmt.Printf("指標變數 *ptr = %d,記憶體地址是:%d\n", *ptr, ptr )
   fmt.Printf("指向指標的指標變數 **pptr = %d,記憶體地址是:%d\n", *pptr, pptr)
   
   //函式呼叫
   changeValue(ptr)
   fmt.Printf("變數age更改后的值 = %d\n", age )

   //通過new函式創建指標
    size := new(int)
    fmt.Printf("size的默認值= %d, 型別是: %T, 地址是: %v\n", *size, size, size)
    *size = 99
    fmt.Println("更改后新的值是:", *size)
}

輸出結果?

特別注意這這兩種傳參的區別

func change(val int) {  
    val = 88
}

func changeValue(val *int) {  
    *val = 66
}

 ?

 

5. 函式型別

函式是基本的代碼塊,用于執行一個任務,

Go 語言最少有個 main() 函式,

你可以通過函式來劃分不同功能,邏輯上每個函式執行的是指定的任務,

函式宣告告訴了編譯器函式的名稱,引數和回傳型別,格式如下

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

函式定義決議:

  • func:函式由關鍵字 func 開始宣告
  • function_name:函式名稱,函式名和引數串列一起構成了函式簽名,
  • parameter list:引數串列,引數就像一個占位符,當函式被呼叫時,你可以將值傳遞給引數,這個值被稱為實際引數,引數串列指定的是引數型別、順序、及引數個數,引數是可選的,也就是說函式也可以不包含引數,
  • return_types:回傳型別,函式回傳一列值,return_types 是該列值的資料型別,有些功能不需要回傳值,這種情況下 return_types 不是必須的,
  • 函式體:函式定義的代碼集合,

5.1  無參無回傳值函式

 package main

 import (
     "fmt"
 )

func hello(){
  fmt.Println("Hello World")
}

 func main() {
    fmt.Println("函式開始了")
    hello()
 }

 5.2  有參無回傳值函式

 package main

 import (
     "fmt"
 )

func hello(message string){
  fmt.Println(message)
}

 func main() {
    fmt.Println("函式開始了")
    hello("hello world")
 }

5.3  無參有回傳值函式

 package main

 import (
     "fmt"
 )

func hello() string {
  return "Hello world"
}


 func main() {
    fmt.Println("函式開始了")
    greeting := hello()
    fmt.Println(greeting)
 }

5.4  有參有回傳值函式

 package main

 import (
     "fmt"
 )

func hello(message string) string {
  return message
}


 func main() {
    fmt.Println("函式開始了")
    greeting := hello("hello world")
    fmt.Println(greeting)
 }

5.5  多個引數多回傳值函式

package main

 import (
     "fmt"
 )

func hello(name string, age int) (string, int) {
  return name, age
}


 func main() {
    fmt.Println("函式開始了")
    name, age := hello("董廣明", 99)

   fmt.Printf("name=%s, age = %d\n", name,age )
 }

5.6  在函式中預定義回傳值的函式

 package main

 import (
     "fmt"
 )

func hello() (message string) {
  message = "hello world!"

  return
}

 func main() {
    fmt.Println("函式開始了")
    greeting := hello()

   fmt.Printf(greeting)
 }

message被定義為回傳變數, 因此,定義的變數message將自動回傳,而無需在最后的return陳述句中定義,

5.7  舍棄回傳值的函式

 package main

 import (
     "fmt"
 )

func hello() (string, string) {
  return "hello world!", "hahaha"
}

 func main() {
    fmt.Println("函式開始了")
   greeting, _ := hello()


   fmt.Printf(greeting)
 }

 5.8  指標傳遞函式

 package main

 import (
     "fmt"
 )

func change(name *string) {
  *name = "dongguangming"
}


 func main() {
    fmt.Println("函式開始了")
    name := "董廣明"
    fmt.Println(name)

    change(&name)
    fmt.Printf(name)
 }

會改變原來的值,這很容易理解

?

5.9 可變引數函式

您可以使用Golang中的…運算子來傳遞陣列,也可以使用相同的…運算子來接收引數,

 package main

 import (
     "fmt"
 )

func print(items ...string) {
  for _, v := range items {
    fmt.Println(v)
  }
}

 func main() {
    fmt.Println("函式開始了")
 
    print("董廣明", "dongguangming", "dmg")

    list := []string{"Hello", "World"}
    print(list...) // An array argument
 }

實際輸出結果?

 

5.10  匿名函式

沒有名字的函式被稱為匿名函式

package main

 import (
     "fmt"
 )

 func main() {
    fmt.Println("函式開始了")
   
    func () {
      fmt.Println("我是一個匿名函式")
    } ()
 }

 

6.  結構體Struct

golang的世界里沒有像java一樣有class類的概念,但它有像C語言一樣的結構體Struct,

結構體是不同欄位的型別化集合,結構體用于將資料分組在一起,

例如,如果我們要對User型別的資料進行分組,則定義一個user的屬性,其中可以包括姓名,年齡,性別,所在城市, 可以使用以下語法定義結構

package main

import "fmt"

type User struct {
     name   string
     age    int
     gender string
     city   string
 }

func main() {
   fmt.Println("結構體開始了")

   user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}

   fmt.Println(user)
   
   //簡寫
   u := User{"董廣明", 31,  "man",  "南京"}

   fmt.Println(u)
   
    //通過指標訪問
   u1 := &User{"董廣明", 31,  "man",  "南京"}

   fmt.Println(u1.name)
}

輸出結果?

 

6.1   方法

 方法是帶有接收器的一種特殊函式, 接收者可以是值或指標, 

package main

import "fmt"

type User struct {
     name   string
     age    int
     gender string
     city   string
 }

// 方法定義
 func (u *User) describe() {
     fmt.Printf("%v 今年 %v歲了\n", u.name, u.age)
 }
 //指標引數
 func (u *User) setAge(age int) {
     u.age = age
 }
 //值引數
 func (u User) setName(name string) {
     u.name = name
 }
 
func main() {
   fmt.Println("結構體開始了")

   user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}

   fmt.Println(user)
   
   user.describe()
   user.setAge(99)
   fmt.Println(user.age)
   user.setName("dongguangming")
   fmt.Println(user.name)
   user.describe()

}

輸出結果?

如圖可以看到,現在可以使用點運算子user.describe呼叫該方法, 注意,接收者是一個指標, 使用指標,我們傳遞了對該值的參考,因此,如果對方法進行任何更改,它將反映在接收器user中,它也不會創建該物件的新副本,從而節省了記憶體,

請注意,在上面的示例中,age的值已更改,而name的值未更改,因為方法setName是接收者型別,而setAge是指標型別,

 

6.2   結構體域欄位匯出

如果欄位名以大寫字母開頭,則定義該結構的包外部的代碼將可對其進行讀寫, 如果該欄位以小寫字母開頭,則只有該結構包中的代碼才能讀取和寫入該欄位,

package main

import "fmt"

type User struct {
    Name string
    Type string

    password string
}

func main() {
    u := User{
        Name: "董廣明",
        Type: "1",

        password: "secret",
    }
    fmt.Println(u.Name, " 級別:", u.Type)
    fmt.Println("密碼是:", u.password)
}

在同一包中,我們可以訪問這些欄位,如本示例所示, 由于main也在主程式包中,因此它可以參考u.password并檢索存盤在其中的值, 通常在結構體中具有未匯出的欄位,并通過匯出的方法來訪問它們,類比java里的訪問權限private域,public訪問方法,

以上程式輸出結果

董廣明  級別: 1
密碼是: secret

6.3  創建匿名結構體

匿名僅創建新的結構變數,而不定義任何新的結構體型別,

package main

import "fmt"


func main() {
  
   emp := struct {
        firstName, lastName string
        age, salary         int
    }{
        firstName: "董",
        lastName:  "廣明",
        age:       31,
        salary:    5000,
    }

    fmt.Println("員工", emp)
}

以上程式代碼中,定義了一個匿名結構變數emp, 正如我們已經提到的,該結構稱為匿名結構,因為它僅創建一個新的結構變數emp,而不定義任何新的結構型別,

輸出結果

員工 {董 廣明 31 5000}

6.4 嵌套結構體

結構可能包含一個欄位,而該欄位又是一個結構, 這些型別的結構稱為嵌套結構,

比如我們經常網上購物時,填寫訂單接收地址(家里,公司,寄存點)

package main

import (  
    "fmt"
)

type Address struct {  
    city, state string
}
type Person struct {  
    name string
    age int
    address Address
}

func main() {  
    var p Person
    p.name = "董廣明"
    p.age = 31
    p.address = Address {
        city: "南京某街道",
        state: "第一識訓地址",
    }
    fmt.Println("名字:", p.name)
    fmt.Println("年齡:",p.age)
    fmt.Println("識訓地址:",p.address.city)
    fmt.Println("是否第一:",p.address.state)
}

上面程式中的Person結構具有一個欄位地址address,該欄位地址又是一個結構體,

 

6.5  指向結構體的指標

您可以使用&運算子獲取指向結構體的指標

package main

import "fmt"

type User struct {
     name   string
     age    int
     gender string
     city   string
 }

func main() {
   fmt.Println("結構體開始了")

   user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}

   fmt.Println(user)
   
    // Pointer to the user struct
	user_point := &user
	fmt.Println(user_point)

	// Accessing struct fields via pointer
	fmt.Println((*user_point).name)
	fmt.Println(user_point.name) // Same as above: No need to explicitly dereference the pointer

	user_point.age = 99
	fmt.Println(user_point)
}

以上示例所示,Go使您可以直接通過指標訪問結構體的欄位, 

輸出結果?

?

 

6.6  結構體是值型別

結構體是值型別,當您將一個結構體變數分配給另一個時,將創建并分配該結構的新副本, 同樣,當您將結構體傳遞給另一個函式時,該函式將獲得其自己的結構副本,

package main

import "fmt"

type User struct {
     name   string
     age    int
     gender string
     city   string
 }
 
func main() {
   fmt.Println("結構體開始了")

   user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}
   user2 := user // A copy of the struct `user` is assigned to `user2`
   fmt.Println("user = ", user)
   fmt.Println("user2 = ", user2)
   
   //
   user2.name = "dongguangming"
   user2.age = 99
   user2.city ="上海"
   fmt.Println("\n更改user2:")
   fmt.Println("user = ", user)
   fmt.Println("user2 = ", user2)
}

輸出結果?

 

7.  介面Interface型別

Go編程提供了另一種稱為介面的資料型別,它表示一組方法簽名,

struct資料型別實作介面中定義的方法,

package main
 
import "fmt"
import "math"

type Shape interface {
   area() float64
}
 
type Rectangle struct{
   height float64
   width float64
}
 
type Circle struct{
   radius float64
}
 
func (r Rectangle) area() float64 {
    return r.height * r.width
}
 
func (c Circle) area() float64 {
    return math.Pi * math.Pow(c.radius, 2)
} 
 
func getArea(shape Shape) float64{
 
    return shape.area()
 
}

func main() {
   fmt.Println("介面開始了")
   rect := Rectangle{20, 50}
   circ := Circle{4}
 
   fmt.Println("長方形面積 =", getArea(rect))
   fmt.Println("圓的面積 =", getArea(circ))
}

輸出結果?

 

8.  Channels型別

通道是一種型別化的管道,可以使用通道運算子<-發送和接收值,

channel<- value    // 發送value值到通道channel
value := <-channel  // 從通道查詢,并把值賦給value

 注意:資料按箭頭方向流動

和其他資料型別類似,通道使用前必須先創建,其初始值是 nil,創建通道的語法格式如下:

var c1 chan [value type]
c1 = make([channel type] [value type], [capacity])
  • [value type] 定義的是 Channel 中所傳輸資料的型別,
  • [channel type] 定義的是 Channel 的型別,其型別有以下三種:
    • “chan” 可讀可寫——“chan int” 則表示可讀寫 int 資料的 channel
    • "chan<-" 僅可寫——“chan<- float64” 則表示僅可寫64位 float 資料的 channel
    • “<-chan” 僅可讀——“<-chan int” 則表示僅可讀 int 資料的 channel
  • [capacity] 是一個可選引數,其定義的是 channel 中的快取區 (buffer),如果不填則默認該 channel 沒有緩沖區 (unbuffered),對于沒有緩沖區的 channel,訊息的發送和收取必須能同時完成,否則會造成阻塞并提示死鎖錯誤,

比如我們想創建了一個讀寫 int 型別,buffer 長度 100 的 channel c1,則如下:

var c1 chan int
c1 = make(chan int, 100)

通過此通道,我們可以發送int型別的資料, 我們可以在此通道中發送和接收資料

package main

 import "fmt"

 func main() {
     ch := make(chan int)
     go func() { ch <- 31 }()
     age := <-ch
     fmt.Println(age)
 }

輸出結果:?

接收方通道等待,直到發送方將資料發送到通道,

 

8.1  單向通道

在有些情況下,我們希望通過通道接收資料但不發送資料, 為此我們還可以創建一個單向通道, 讓我們看一個簡單的例子:

package main

 import (
     "fmt"
 )

 func main() {
     ch := make(chan string)
     go sc(ch)
     fmt.Println(<-ch)
 }

 func sc(ch chan<- string) {
     ch <- "你好,董廣明"
 }

在上面的示例代碼中,sc是go協程,該例程只能將訊息發送到通道,但不能接收訊息,

執行代碼輸出結果

$go run main.go
你好,董廣明

 

8.2  快取通道(Buffered channel

在Golang中可以創建一個緩沖通道, 對于緩沖的通道,如果緩沖區已滿,則將阻止發送到該通道的訊息, 讓我們看一個小例子

package main

 import "fmt"

 func main() {
     ch := make(chan string, 2)
     ch <- "hello"
     ch <- "world"
     
     ch <- "!!!" // 超了緩沖最大值要報錯
     fmt.Println(<-ch)
 }

實際上超過緩沖最大值,要報錯

?

那怎么辦呢,還好有以下解決方法

package main

 import "fmt"

 func main() {
     ch := make(chan string, 2)
     ch <- "hello"
     ch <- "world"

     //創建匿名函式
     function := func(name string) { ch <- name }

     //
     go function("董廣明") 
     go function("donguangming") 
     go function("dgm") 
     go function("dgmdgm") 
     go function("3dgm") 

     fmt.Println(<-ch)
     fmt.Println(<-ch)
     fmt.Println(<-ch)
     fmt.Println(<-ch)
     fmt.Println(<-ch)
     fmt.Println(<-ch)
     fmt.Println(<-ch)
 }

輸出結果?

 

 

測驗結果

golang測驗結果得分?

 

總結: golang就是綜合性編程語言,幸好以前做過java、pyhon、JavaScript開發,上學時又學過C語言(第一編程語言),故學起golang很快,極少部分是golang本身特有的,

一句話概括:golang是幾種語言的混合體,外加自己的特性,如果不考慮語言本身,你會覺得像是在寫C,有時又像是寫python,寫函式時又像JavaScript中函式的變體,

 

參考:

  1.  Golang Tutorial?—?from zero to hero  https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/

  2. Understanding Maps in Go   https://www.digitalocean.com/community/tutorials/understanding-maps-in-go

  3. Golang Maps  https://www.geeksforgeeks.org/golang-maps/

  4. Golang Tutorial – Learn Golang by Examples https://www.edureka.co/blog/golang-tutorial/#map

  5. The anatomy of Slices in Go  https://medium.com/rungo/the-anatomy-of-slices-in-go-6450e3bb2b94

  6. GoLang Tutorial - Structs and receiver methods - 2020 https://www.bogotobogo.com/GoLang/GoLang_Structs.php

  7. Golang Cheatsheet: Functions https://ado.xyz/blog/golang-cheatsheet-functions/

  8. Ultimate Guide to Go Variadic Functions  https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Fblog.learngoprogramming.com%2Fgolang-variadic-funcs-how-to-patterns-369408f19085

  9. Golang Methods Tutorial with Examples https://www.callicoder.com/golang-methods-tutorial/

  10. Go Best Practices: Should you use a method or a function? https://flaviocopes.com/golang-methods-or-functions/

  11. Methods that satisfy interfaces in golang https://suraj.io/post/golang-methods-interfaces/

  12.  Pass by pointer vs pass by value in Go https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/

  13. Go Data Structures: Interfaces https://research.swtch.com/interfaces

  14. How to Define and Implement a Go Interface https://code.tutsplus.com/tutorials/how-to-define-and-implement-a-go-interface--cms-28962

  15. Methods and Interfaces in Go https://dev-pages.info/golang-interfaces/

  16. Go (Golang) - understanding the object oriented features with structs, methods, and interfaces https://unixsheikh.com/articles/go-understanding-the-object-oriented-features-with-structs-methods-and-interfaces.html#interfaces-in-go

  17. 理解 Golang 的 Channel 型別 https://studygolang.com/articles/25805

  18. Anatomy of Channels in Go - Concurrency in Go  https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3657.html

標籤:Go

上一篇:Go的100天之旅-05常量

下一篇:spring原始碼決議---一文講透@Configuration

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more