天天看點

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

複合類型

文章目錄

  • 複合類型
  • 前言
  • 分類
  • 指針
    • 基本操作
    • new函數
    • 指針做函數參數
  • 數組
    • 概述
    • 操作數組
    • 在函數間傳遞數組
  • 切片
    • 切片概述
    • 切片的建立和初始化
    • 切片的操作
    • 切片做函數參數
  • map
    • 概述
    • 建立和初始化
    • 常用操作
    • map做函數參數
  • 結構體
    • 結構體類型
    • 結構體初始化
    • 結構體成員的使用
    • 結構體比較
    • 結構體作為函數參數
  • 可見性
  • 随機數的使用
  • 冒泡排序
  • 猜數字遊戲
  • 數組,切片,map,結構體的差別

前言

主要是介紹go語言複合資料類型,包括指針,切片

分類

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

指針

指針是一個代表着某個記憶體位址的值。這個記憶體位址往往是在記憶體中存儲的另一個變量的值的起始位置。Go語言對指針的支援介于Java語言和C/C++語言之間,它既沒有像Java語言那樣取消了代碼對指針的直接操作的能力,也避免了C/C++語言中由于對指針的濫用而造成的安全和可靠性問題

基本操作

Go語言雖然保留了指針,但與其它程式設計語言不同的是:

 預設值 nil,沒有 NULL 常量

 操作符 “&” 取變量位址, “*” 通過指針通路目标對象

 不支援指針運算,不支援 “->” 運算符,直接⽤ “.” 通路目标成員

func main() {
    var a int = 10              //聲明一個變量,同時初始化
    fmt.Printf("&a = %p\n", &a) //操作符 "&" 取變量位址

    var p *int = nil //聲明一個變量p, 類型為 *int, 指針類型
    p = &a
    fmt.Printf("p = %p\n", p)
    fmt.Printf("a = %d, *p = %d\n", a, *p)

    *p = 111 //*p操作指針所指向的記憶體,即為a
    fmt.Printf("a = %d, *p = %d\n", a, *p)
}
           

注意不要操作沒有合法指向的記憶體,即不能給野指針指派

package main //必須有個main包

import "fmt"

func main() {
	var p *int
	p = nil
	fmt.Println("p = ", p)

	//*p = 666 //err, 因為p沒有合法指向

	var a int
	p = &a //p指向a
	*p = 666
	fmt.Println("a = ", a)

}
           

變量記憶體和變量位址的差別

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

指針操作的原理

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

new函數

表達式new(T)将建立一個T類型的匿名變量,所做的是為T類型的新值配置設定并清零一塊記憶體空間,然後将這塊記憶體空間的位址作為結果傳回,而這個結果就是指向這個新的T類型值的指針值,傳回的指針類型為*T。

func main() {
    var p1 *int
    p1 = new(int)              //p1為*int 類型, 指向匿名的int變量
    fmt.Println("*p1 = ", *p1) //*p1 =  0

    p2 := new(int) //p2為*int 類型, 指向匿名的int變量
    *p2 = 111
    fmt.Println("*p2 = ", *p2) //*p1 =  111
}
           
package main //必須有個main包

import "fmt"

func main() {
	//a := 10 //整型變量a

	var p *int
	//指向一個合法記憶體
	//p = &a

	//p是*int, 指向int類型
	p = new(int)

	*p = 666
	fmt.Println("*p = ", *p)

	q := new(int) //自動推導類型
	*q = 777
	fmt.Println("*q = ", *q)

}

           

我們隻需使用new()函數,無需擔心其記憶體的生命周期或怎樣将其删除,因為Go語言的記憶體管理系統會幫我們打理一切。

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

指針做函數參數

func swap01(a, b int) {
    a, b = b, a
    fmt.Printf("swap01 a = %d, b = %d\n", a, b)
}

func swap02(x, y *int) {
    *x, *y = *y, *x
}

func main() {
    a := 10
    b := 20

    //swap01(a, b) //值傳遞
    swap02(&a, &b) //變量位址傳遞
    fmt.Printf("a = %d, b = %d\n", a, b)
}

           

對比一下,普通變量做函數參數

package main //必須有個main包

import "fmt"

func swap(a, b int) {
	a, b = b, a
	fmt.Printf("swap: a = %d, b = %d\n", a, b)
}

func main() {
	a, b := 10, 20

	//通過一個函數交換a和b的内容
	swap(a, b) //變量本身傳遞,值傳遞(站在變量角度)
	fmt.Printf("main: a = %d, b = %d\n", a, b)
}

           

數組

概述

數組是指一系列同一類型資料的集合。數組中包含的每個資料被稱為數組元素(element),一個數組包含的元素個數被稱為數組的長度。

數組⻓度必須是常量,且是類型的組成部分。 [2]int 和 [3]int 是不同類型

var n int = 10
    var a [n]int  //err, non-constant array bound n
    var b [10]int //ok

           

數組的基本使用

package main //必須有個main包

import "fmt"

func main() {
	//定義一個數組, [10]int和[5]int是不同類型
	//[數字], 這個數字作為數組元素個數
	var a [10]int
	var b [5]int

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

	//注意:定義數組是,指定的數組元素個數必須是常量
	//n := 10
	//var c [n]int //err non-constant array bound n

	//操作數組元素,從0開始,到len()-1, 不對稱元素,這個數字,叫下标
	//這是下标,可以是變量或常量
	a[0] = 1
	i := 1
	a[i] = 2 // a[1] = 2

	//指派,每個元素
	for i := 0; i < len(a); i++ {
		a[i] = i + 1
	}

	//列印
	//第一個傳回下标,第二個傳回元素
	for i, data := range a {
		fmt.Printf("a[%d] = %d\n", i, data)
	}

}

           

操作數組

數組的每個元素可以通過索引下标來通路,索引下标的範圍是從0開始到數組長度減1的位置。

var a [10]int
    for i := 0; i < 10; i++ {
        a[i] = i + 1
        fmt.Printf("a[%d] = %d\n", i, a[i])
    }

    //range具有兩個傳回值,第一個傳回值是元素的數組下标,第二個傳回值是元素的值
    for i, v := range a {
        fmt.Println("a[", i, "]=", v)
    }
           

内置函數 len(長度) 和 cap(容量) 都傳回數組⻓度 (元素數量):

a := [10]int{}
    fmt.Println(len(a), cap(a))//10 10
           

初始化:

a := [3]int{1, 2}           // 未初始化元素值為 0
    b := [...]int{1, 2, 3}      // 通過初始化值确定數組長度
    c := [5]int{2: 100, 4: 200} // 通過索引号初始化元素,未初始化元素值為 0
    fmt.Println(a, b, c)        //[1 2 0] [1 2 3] [0 0 100 0 200]

    //支援多元數組
    d := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
    e := [...][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} //第二維不能寫"..."
    f := [4][2]int{1: {20, 21}, 3: {40, 41}}
    g := [4][2]int{1: {0: 20}, 3: {1: 41}}
    fmt.Println(d, e, f, g)
           
package main //必須有個main包

import "fmt"

func main() {
	//聲明定義同時指派,叫初始化
	//1、全部初始化
	var a [5]int = [5]int{1, 2, 3, 4, 5}
	fmt.Println("a = ", a)

	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println("b = ", b)

	//部分初始化,沒有初始化的元素,自動指派為0
	c := [5]int{1, 2, 3}
	fmt.Println("c = ", c)

	//指定某個元素初始化
	d := [5]int{2: 10, 4: 20}
	fmt.Println("d = ", d)

}

           
package main //必須有個main包

import "fmt"

func main() {
	//有多少個[]就是多少維
	//有多少個[]就用多少個循環
	var a [3][4]int

	k := 0
	for i := 0; i < 3; i++ {
		for j := 0; j < 4; j++ {
			k++
			a[i][j] = k
			fmt.Printf("a[%d][%d] = %d, ", i, j, a[i][j])
		}
		fmt.Printf("\n")
	}

	fmt.Println("a = ", a)

	//有3個元素,每個元素又是一維數組[4]int
	b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}
	fmt.Println("b = ", b)

	//部分初始化,沒有初始化的值為0
	c := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}, {9, 10}}
	fmt.Println("c = ", c)

	d := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
	fmt.Println("d = ", d)

	e := [3][4]int{1: {5, 6, 7, 8}}
	fmt.Println("e = ", e)
}

           

相同類型的數組之間可以使用 == 或 != 進行比較,但不可以使用 < 或 >,也可以互相指派:

a := [3]int{1, 2, 3}
    b := [3]int{1, 2, 3}
    c := [3]int{1, 2}
    fmt.Println(a == b, b == c) //true false

    var d [3]int
    d = a
    fmt.Println(d) //[1 2 3]

           

在函數間傳遞數組

根據記憶體和性能來看,在函數間傳遞數組是一個開銷很大的操作。在函數之間傳遞變量時,總是以值的方式傳遞的。如果這個變量是一個數組,意味着整個數組,不管有多長,都會完整複制,并傳遞給函數。

func modify(array [5]int) {
    array[0] = 10 // 試圖修改數組的第一個元素
    //In modify(), array values: [10 2 3 4 5]
    fmt.Println("In modify(), array values:", array)
}

func main() {
    array := [5]int{1, 2, 3, 4, 5} // 定義并初始化一個數組
    modify(array)                  // 傳遞給一個函數,并試圖在函數體内修改這個數組内容
    //In main(), array values: [1 2 3 4 5]
    fmt.Println("In main(), array values:", array)
}

           

數組指針做函數參數:

如果用數組指針做參數,在傳參的函數調用傳回後,也會修改原始數組裡面的值

func modify(array *[5]int) {
    (*array)[0] = 10
    //In modify(), array values: [10 2 3 4 5]
    fmt.Println("In modify(), array values:", *array)
}

func main() {
    array := [5]int{1, 2, 3, 4, 5} // 定義并初始化一個數組
    modify(&array)                 // 數組指針
    //In main(), array values: [10 2 3 4 5]
    fmt.Println("In main(), array values:", array)
}

           

切片

切片概述

數組的長度在定義之後無法再次修改;數組是值類型,每次傳遞都将産生一份副本。顯然這種資料結構無法完全滿足開發者的真實需求。Go語言提供了數組切片(slice)來彌補數組的不足。

切片并不是數組或數組指針,它通過内部指針和相關屬性引⽤數組⽚段,以實作變⻓⽅案。

slice并不是真正意義上的動态數組,而是一個引用類型。slice總是指向一個底層array,slice的聲明也可以像array一樣,隻是不需要長度。

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

注意數組的初始化方式

array := [...]int{10,40,30}

根據值來進行初始化

切片的建立和初始化

slice和數組的差別:聲明數組時,方括号内寫明了數組的長度或使用…自動計算長度,而聲明slice時,方括号内沒有任何字元。

var s1 []int //聲明切片和聲明array一樣,隻是少了長度,此為空(nil)切片
    s2 := []int{}

    //make([]T, length, capacity) //capacity省略,則和length的值相同
    var s3 []int = make([]int, 0)
    s4 := make([]int, 0, 0)    

    s5 := []int{1, 2, 3} //建立切片并初始化

           

注意:make隻能建立slice、map和channel,并且傳回一個有初始值(非零)。

Go的new和make配置設定記憶體的差別

1.new(T)建立一個沒有任何資料的類型為T的執行個體,并傳回該執行個體的指針;

2.make(T, args)隻能建立 slice、map和channel,并且傳回一個有初始值args(非零)的T類型的執行個體,非指針。

var p *[]int = new([]int)   // 配置設定slice結構記憶體 : *p = nil
var v []int = make([]int, 3)  // 配置設定一個有 3 個整數的slice

列印結果是[0,0,0]
           

切片的操作

(1)切片截取

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

示例說明:

array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別
package main //必須有個main包

import "fmt"

func main() {
	array := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	//[low:high:max] 取下标從low開始的元素, len=high-low, cap=max-low
	s1 := array[:] //[0:len(array):len(array)] 不指定容量和長度一樣
	fmt.Println("s1 = ", s1)
	fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

	//操作某個元素,和數組操作方式一樣
	data := array[1]
	fmt.Println("data = ", data)

	s2 := array[3:6:7] //a[3], a[4], a[5]   len = 6-3=3    cap = 7-3=4
	fmt.Println("s2 = ", s2)
	fmt.Printf("len = %d, cap = %d\n", len(s2), cap(s2))

	s3 := array[:6] //從0開始,去6個元素,容量為10, 常用
	fmt.Println("s3 = ", s3)
	fmt.Printf("len = %d, cap = %d\n", len(s3), cap(s3))

	s4 := array[3:] //從下标為3開始,到結尾
	fmt.Println("s4 = ", s4)
	fmt.Printf("len = %d, cap = %d\n", len(s4), cap(s4))

}

           

(2)切片和底層數組關系

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    s1 := s[2:5]       //[2 3 4]
    s1[2] = 100        //修改切片某個元素改變底層數組
    fmt.Println(s1, s) //[2 3 100] [0 1 2 3 100 5 6 7 8 9]

    s2 := s1[2:6] // 新切片依舊指向原底層數組 [100 5 6 7]
    s2[3] = 200
    fmt.Println(s2) //[100 5 6 200]

    fmt.Println(s) //[0 1 2 3 100 5 6 200 8 9]
           

(3)内建函數

append函數:向 slice 尾部添加資料,傳回新的 slice 對象:

var s1 []int //建立nil切換
//s1 := make([]int, 0)
s1 = append(s1, 1)       //追加1個元素
s1 = append(s1, 2, 3)    //追加2個元素
s1 = append(s1, 4, 5, 6) //追加3個元素
fmt.Println(s1)          //[1 2 3 4 5 6]

s2 := make([]int, 5)
s2 = append(s2, 6)
fmt.Println(s2) //[0 0 0 0 0 6]

s3 := []int{1, 2, 3}
s3 = append(s3, 4, 5)
fmt.Println(s3)//[1 2 3 4 5]
           

append函數會智能地底層數組的容量增長,一旦超過原底層數組容量,通常以2倍容量重新配置設定底層數組,并複制原來的資料:

func main() {
    s := make([]int, 0, 1)
    c := cap(s)
    for i := 0; i < 50; i++ {
        s = append(s, i)
        if n := cap(s); n > c {
            fmt.Printf("cap: %d -> %d\n", c, n)
            c = n
        }
    }
    /*
        cap: 1 -> 2
        cap: 2 -> 4
        cap: 4 -> 8
        cap: 8 -> 16
        cap: 16 -> 32
        cap: 32 -> 64
    */
}
           

copy函數 :copy 在兩個 slice 間複制資料,複制⻓度以 len 小的為準,兩個 slice 可指向同⼀底層數組。

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := data[8:]  //{8, 9}
s2 := data[:5] //{0, 1, 2, 3, 4}
copy(s2, s1)    // dst:s2, src:s1

fmt.Println(s2)   //[8 9 2 3 4]
fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9]
           

切片做函數參數

func test(s []int) { //切片做函數參數
    s[0] = -1
    fmt.Println("test : ")
    for i, v := range s {
        fmt.Printf("s[%d]=%d, ", i, v)
        //s[0]=-1, s[1]=1, s[2]=2, s[3]=3, s[4]=4, s[5]=5, s[6]=6, s[7]=7, s[8]=8, s[9]=9,
    }
    fmt.Println("\n")
}

func main() {
    slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    test(slice)

    fmt.Println("main : ")
    for i, v := range slice {
        fmt.Printf("slice[%d]=%d, ", i, v)
        //slice[0]=-1, slice[1]=1, slice[2]=2, slice[3]=3, slice[4]=4, slice[5]=5, slice[6]=6, slice[7]=7, slice[8]=8, slice[9]=9,
    }
    fmt.Println("\n")
}
           

map

概述

Go語言中的map(映射、字典)是一種内置的資料結構,它是一個無序的key—value對的集合,比如以身份證号作為唯一鍵來辨別一個人的資訊。

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

map格式為:

map[keyType]valueType
           

在一個map裡所有的鍵都是唯一的,而且必須是支援==和!=操作符的類型,切片、函數以及包含切片的結構類型這些類型由于具有引用語義,不能作為映射的鍵,使用這些類型會造成編譯錯誤:

map值可以是任意類型,沒有限制。map裡所有鍵的資料類型必須是相同的,值也必須如何,但鍵和值的資料類型可以不相同。

注意:map是無序的,我們無法決定它的傳回順序,是以,每次列印結果的順利有可能不同。

建立和初始化

(1)map的建立

var m1 map[int]string  //隻是聲明一個map,沒有初始化, 此為空(nil)map
fmt.Println(m1 == nil) //true
//m1[1] = "mike" //err, panic: assignment to entry in nil map

//m2, m3的建立方法是等價的
m2 := map[int]string{}
m3 := make(map[int]string)
fmt.Println(m2, m3) //map[] map[]

m4 := make(map[int]string, 10) //第2個參數指定容量
fmt.Println(m4)                //map[]
           

(2)初始化

//1、定義同時初始化
var m1 map[int]string = map[int]string{1: "mike", 2: "yoyo"}
fmt.Println(m1) //map[1:mike 2:yoyo]

//2、自動推導類型 :=
m2 := map[int]string{1: "mike", 2: "yoyo"}
fmt.Println(m2)
           

常用操作

(1) 指派

m1 := map[int]string{1: "mike", 2: "yoyo"}
m1[1] = "xxx"   //修改
m1[3] = "lily"  //追加, go底層會自動為map配置設定空間
fmt.Println(m1) //map[1:xxx 2:yoyo 3:lily]

m2 := make(map[int]string, 10) //建立map
m2[0] = "aaa"
m2[1] = "bbb"
fmt.Println(m2)           //map[0:aaa 1:bbb]
fmt.Println(m2[0], m2[1]) //aaa bbb
           

(2) 周遊

m1 := map[int]string{1: "mike", 2: "yoyo"}
//疊代周遊1,第一個傳回值是key,第二個傳回值是value
for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //2 ----> yoyo
}

//疊代周遊2,第一個傳回值是key,第二個傳回值是value(可省略)
for k := range m1 {
    fmt.Printf("%d ----> %s\n", k, m1[k])
    //1 ----> mike
    //2 ----> yoyo
}

//判斷某個key所對應的value是否存在, 第一個傳回值是value(如果存在的話)
value, ok := m1[1]
fmt.Println("value = ", value, ", ok = ", ok) //value =  mike , ok =  true

value2, ok2 := m1[3]
fmt.Println("value2 = ", value2, ", ok2 = ", ok2) //value2 =   , ok2 =  false
           

(3)删除

m1 := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}
//疊代周遊1,第一個傳回值是key,第二個傳回值是value
for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //2 ----> yoyo
    //3 ----> lily
}

delete(m1, 2) //删除key值為3的map

for k, v := range m1 {
    fmt.Printf("%d ----> %s\n", k, v)
    //1 ----> mike
    //3 ----> lily
}
           

map做函數參數

在函數間傳遞映射并不會制造出該映射的一個副本,不是值傳遞,而是引用傳遞:

func DeleteMap(m map[int]string, key int) {
    delete(m, key) //删除key值為3的map

    for k, v := range m {
        fmt.Printf("len(m)=%d, %d ----> %s\n", len(m), k, v)
        //len(m)=2, 1 ----> mike
        //len(m)=2, 3 ----> lily
    }
}

func main() {
    m := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}

    DeleteMap(m, 2) //删除key值為3的map

    for k, v := range m {
        fmt.Printf("len(m)=%d, %d ----> %s\n", len(m), k, v)
        //len(m)=2, 1 ----> mike
        //len(m)=2, 3 ----> lily
    }
}
           

結構體

結構體類型

有時我們需要将不同類型的資料組合成一個有機的整體,如:一個學生有學号/姓名/性别/年齡/位址等屬性。顯然單獨定義以上變量比較繁瑣,資料不便于管理。

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

結構體是一種聚合的資料類型,它是由一系列具有相同類型或不同類型的資料構成的資料集合。每個資料稱為結構體的成員。

結構體初始化

(1) 普通變量

type Student struct {
    id   int
    name string
    sex  byte
    age  int
    addr string
}

func main() {
    //1、順序初始化,必須每個成員都初始化
    var s1 Student = Student{1, "mike", 'm', 18, "sz"}
    s2 := Student{2, "yoyo", 'f', 20, "sz"}
    //s3 := Student{2, "tom", 'm', 20} //err, too few values in struct initializer

    //2、指定初始化某個成員,沒有初始化的成員為零值
    s4 := Student{id: 2, name: "lily"}
}
           

(2) 指針變量

type Student struct {
    id   int
    name string
    sex  byte
    age  int
    addr string
}

func main() {
    var s5 *Student = &Student{3, "xiaoming", 'm', 16, "bj"}
    s6 := &Student{4, "rocco", 'm', 3, "sh"}
}
           

結構體成員的使用

(1) 普通變量

//===============結構體變量為普通變量
    //1、列印成員
    var s1 Student = Student{1, "mike", 'm', 18, "sz"}
    //結果:id = 1, name = mike, sex = m, age = 18, addr = sz
    fmt.Printf("id = %d, name = %s, sex = %c, age = %d, addr = %s\n", s1.id, s1.name, s1.sex, s1.age, s1.addr)

    //2、成員變量指派
    var s2 Student
    s2.id = 2
    s2.name = "yoyo"
    s2.sex = 'f'
    s2.age = 16
    s2.addr = "guangzhou"
    fmt.Println(s2) //{2 yoyo 102 16 guangzhou}
           

(2)指針變量

//===============結構體變量為指針變量
    //3、先配置設定空間,再指派
    s3 := new(Student)
    s3.id = 3
    s3.name = "xxx"
    fmt.Println(s3) //&{3 xxx 0 0 }

    //4、普通變量和指針變量類型列印
    var s4 Student = Student{4, "yyy", 'm', 18, "sz"}
    fmt.Printf("s4 = %v, &s4 = %v\n", s4, &s4) //s4 = {4 yyy 109 18 sz}, &s4 = &{4 yyy 109 18 sz}

    var p *Student = &s4
    //p.成員 和(*p).成員 操作是等價的
    p.id = 5
    (*p).name = "zzz"
    fmt.Println(p, *p, s4) //&{5 zzz 109 18 sz} {5 zzz 109 18 sz} {5 zzz 109 18 sz}
           

結構體比較

如果結構體的全部成員都是可以比較的,那麼結構體也是可以比較的,那樣的話兩個結構體将可以使用 == 或 != 運算符進行比較,但不支援 > 或 < 。

func main() {
    s1 := Student{1, "mike", 'm', 18, "sz"}
    s2 := Student{1, "mike", 'm', 18, "sz"}

    fmt.Println("s1 == s2", s1 == s2) //s1 == s2 true
    fmt.Println("s1 != s2", s1 != s2) //s1 != s2 false
}
           

結構體作為函數參數

(1) 值傳遞

func printStudentValue(tmp Student) {
    tmp.id = 250
    //printStudentValue tmp =  {250 mike 109 18 sz}
    fmt.Println("printStudentValue tmp = ", tmp)
}

func main() {
var s Student = Student{1, "mike", 'm', 18, "sz"}

    printStudentValue(s)        //值傳遞,形參的修改不會影響到實參
    fmt.Println("main s = ", s) //main s =  {1 mike 109 18 sz}
}
           

(2)引用傳遞

func printStudentPointer(p *Student) {
    p.id = 250
    //printStudentPointer p =  &{250 mike 109 18 sz}
    fmt.Println("printStudentPointer p = ", p)
}

func main() {
    var s Student = Student{1, "mike", 'm', 18, "sz"}

    printStudentPointer(&s)     //引用(位址)傳遞,形參的修改會影響到實參
    fmt.Println("main s = ", s) //main s =  {250 mike 109 18 sz}
}
           

可見性

Go語言對關鍵字的增加非常吝啬,其中沒有private、 protected、 public這樣的關鍵字。

要使某個符号對其他包(package)可見(即可以通路),需要将該符号定義為以大寫字母

開頭。

目錄結構:

【GO】複合資料類型複合類型前言分類指針數組切片map結構體可見性随機數的使用冒泡排序猜數字遊戲數組,切片,map,結構體的差別

test.go示例代碼如下:

//test.go
package test

//student01隻能在本檔案件引用,因為首字母小寫
type student01 struct {
    Id   int
    Name string
}

//Student02可以在任意檔案引用,因為首字母大寫
type Student02 struct {
    Id   int
    name string
}
           

main.go示例代碼如下:

// main.go
package main

import (
    "fmt"
    "test" //導入test包
)

func main() {
    //s1 := test.student01{1, "mike"} //err, cannot refer to unexported name test.student01

    //err, implicit assignment of unexported field 'name' in test.Student02 literal
    //s2 := test.Student02{2, "yoyo"}
    //fmt.Println(s2)

    var s3 test.Student02 //聲明變量
    s3.Id = 1             //ok
    //s3.name = "mike"  //err, s3.name undefined (cannot refer to unexported field or method name)
    fmt.Println(s3)
}
           

例子2

package test

import "fmt"

//如果首字母是小寫,隻能在同一個包裡使用
type stu struct {
	id int
}

type Stu struct {
	//id int //如果首字母是小寫,隻能在同一個包裡使用
	Id int
}

//如果首字母是小寫,隻能在同一個包裡使用
func myFunc() {
	fmt.Println("this is myFunc")
}

func MyFunc() {
	fmt.Println("this is MyFunc -=======")
}

           

例子2 main函數

package main //必須有個main包

import "test"
import "fmt"

func main() {
	//包名.函數名
	test.MyFunc()

	//包名.結構體裡類型名
	var s test.Stu
	s.Id = 666
	fmt.Println("s = ", s)
}
           

随機數的使用

package main //必須有個main包

import "fmt"
import "math/rand"
import "time"

func main() {
	//設定種子, 隻需一次
	//如果種子參數一樣,每次運作程式産生的随機數都一樣
	rand.Seed(time.Now().UnixNano()) //以目前系統時間作為種子參數

	for i := 0; i < 5; i++ {

		//産生随機數
		//fmt.Println("rand = ", rand.Int()) //随機很大的數
		fmt.Println("rand = ", rand.Intn(100)) //限制在100内的數
	}

}

           

冒泡排序

package main //必須有個main包

import "fmt"
import "math/rand"
import "time"

func main() {
	//設定種子, 隻需一次
	rand.Seed(time.Now().UnixNano())

	var a [10]int
	n := len(a)

	for i := 0; i < n; i++ {
		a[i] = rand.Intn(100) //100以内的随機數
		fmt.Printf("%d, ", a[i])
	}
	fmt.Printf("\n")

	//冒泡排序,挨着的2個元素比較,升序(大于則交換)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-1-i; j++ {
			if a[j] > a[j+1] {
				a[j], a[j+1] = a[j+1], a[j]
			}
		}
	}

	fmt.Printf("\n排序後:\n")
	for i := 0; i < n; i++ {
		fmt.Printf("%d, ", a[i])
	}
	fmt.Printf("\n")

}

           

猜數字遊戲

package main //必須有個main包

import "fmt"
import "math/rand"
import "time"

func CreatNum(p *int) {
	//設定種子
	rand.Seed(time.Now().UnixNano())

	var num int
	for {
		num = rand.Intn(10000) //一定是4位數
		if num >= 1000 {
			break
		}
	}

	//fmt.Println("num = ", num)

	*p = num

}

func GetNum(s []int, num int) {
	s[0] = num / 1000       //取千位
	s[1] = num % 1000 / 100 //取百位
	s[2] = num % 100 / 10   //取百位
	s[3] = num % 10         //取個位
}

func OnGame(randSlice []int) {
	var num int
	keySlice := make([]int, 4)

	for {
		for {
			fmt.Printf("請輸入一個4位數:")
			fmt.Scan(&num)

			// 999 < num < 10000
			if 999 < num && num < 10000 {
				break
			}

			fmt.Println("請輸入的數不符合要求")
		}
		//fmt.Println("num = ", num)
		GetNum(keySlice, num)
		//fmt.Println("keySlice = ", keySlice)

		n := 0
		for i := 0; i < 4; i++ {
			if keySlice[i] > randSlice[i] {
				fmt.Printf("第%d位大了一點\n", i+1)
			} else if keySlice[i] < randSlice[i] {
				fmt.Printf("第%d位小了一點\n", i+1)
			} else {
				fmt.Printf("第%d位猜對了\n", i+1)
				n++
			}
		}

		if n == 4 { //4位都猜對了
			fmt.Println("全部猜對!!!")
			break //跳出循環
		}
	}
}

func main() {
	var randNum int

	//産生一個4位的随機數
	CreatNum(&randNum)
	//fmt.Println("randNum: ", randNum)

	randSlice := make([]int, 4)
	//儲存這個4位數的每一位
	GetNum(randSlice, randNum)
	//fmt.Println("randSlice = ", randSlice)

	/*
		n1 := 1234 / 1000 //取商
		//(1234 % 1000) //取餘數,結果為234   234/100取商得到2
		n2 := 1234 % 1000 / 100
		fmt.Println("n1 = ", n1)
		fmt.Println("n2 = ", n2)
	*/

	OnGame(randSlice) //遊戲

}

           

數組,切片,map,結構體的差別

(1)數組和結構體做函數參數,支援值傳遞和引用傳遞(指針)

(2)切片和map作函數參數是引用傳遞

go