復(fù)合類型的分類
指針、數(shù)組、切片slice、字典map、結(jié)構(gòu)體struct
指針
package main
import "fmt"
func main() {
fmt.Println("指針使用演示案例")
var a int
a = 777
fmt.Println("a = ", a) //變量的內(nèi)存
fmt.Println("&a = ", &a) //變量的地址
// 1. 指針的基本使用
var p *int
p = &a
fmt.Printf("a的地址:%v,a的值:%v\n", p, *p) //a的地址:0xc00000a0c0,a的值:777
*p = 666
fmt.Println("a = ", a)
// 2. 不要操作沒有合法指向的內(nèi)存
p = nil
// *p = 88 //err,panic: runtime error: invalid memory address or nil pointer dereference
// 3. new函數(shù)的使用
// 表達(dá)式new(T)將創(chuàng)建一個(gè)T類型的匿名變量,所做的是為T類型的新值分配并清零一塊內(nèi)存空間,
//然后將這塊內(nèi)存空間的地址作為結(jié)果返回,而這個(gè)結(jié)果就是指向這個(gè)新的T類型值的指針值,返回的指針類型為*T。
//我們只需使用new()函數(shù),無需擔(dān)心其內(nèi)存的生命周期或怎樣將其刪除,因?yàn)镚o語言的內(nèi)存管理系統(tǒng)會(huì)幫我們打理一切。
p = new(int)
fmt.Printf("p的地址:%v,p的值:%v\n", p, *p) //p的地址:0xc00000a0e0,p的值:0
// 4. 地址傳遞-交換兩個(gè)數(shù)的值
i, j := 3, 4
fmt.Printf("i的值:%v,j的值:%v\n", i, j)
swap(&i, &j)
fmt.Printf("交換后,i的值:%v,j的值:%v\n", i, j)
}
func swap(a, b *int) {
*a, *b = *b, *a
}
數(shù)組
數(shù)組是指一系列同一類型數(shù)據(jù)的集合。數(shù)組中包含的每個(gè)數(shù)據(jù)被稱為數(shù)組元素(element),一個(gè)數(shù)組包含的元素個(gè)數(shù)被稱為數(shù)組的長(zhǎng)度。
數(shù)組?度必須是常量,且是類型的組成部分。 [2]int 和 [3]int 是不同類型。
package main
import "fmt"
func main() {
fmt.Println("數(shù)組使用演示案例")
var arr [10]int
for i := 0; i < len(arr); i++ {
arr[i] = i * i
}
for _, data := range arr {
fmt.Println(data)
}
// 1. 數(shù)組的初始化
var n = 10
//var arr1 [n]int //err, non-constant array bound n
const n1 = 10
var arr3 [n1]int //ok
fmt.Println(n, arr3)
var arr4 [5]int = [5]int{1, 2, 3, 4, 5} // 全部初始化
fmt.Println("arr4 = ", arr4)
var arr5 [5]int = [5]int{1, 2, 3} // 部分初始化
fmt.Println("arr5 = ", arr5)
var arr6 [5]int = [5]int{1: 77, 2: 88} //指定元素初始化
fmt.Println("arr6 = ", arr6)
// 2. 二維數(shù)組
var aa [3][4]int
for i := 0; i < 3; i++ {
for j := 0; j < 4; j++ {
aa[i][j] = i + j
}
}
fmt.Println("aa = ", aa)
bb := [3][4]int{{1, 2, 3, 4}, {4, 3, 2, 1}, {3, 3}}
fmt.Println("bb = ", bb)
// 多維數(shù)組的部分初始化
cc := [3][4]int{1: {4, 3, 2, 1}}
fmt.Println("cc = ", cc)
// 3. 數(shù)組的比較和賦值
//相同類型的數(shù)組之間可以使用 == 或 != 進(jìn)行比較,但不可以使用 < 或 >,也可以相互賦值:
var x1 [5]int = [5]int{1, 2, 3, 4, 5}
var x2 [5]int = [5]int{1, 2, 3, 4, 5}
var x3 [3]int = [3]int{1, 2, 3}
fmt.Println("x1 == x2 ? ", x1 == x2) //err, invalid operation: x1 == x3 (mismatched types [5]int and [3]int)
fmt.Println("x1 == x3 ? ", x1 == x3) //err, invalid operation: x1 == x3 (mismatched types [5]int and [3]int)
xx1 := [5]int{1, 2, 3, 4, 5}
xx2 := [5]int{1, 2, 3, 4, 5}
fmt.Println("xx1 == xx2 ? ", xx1 == xx2) //true
}
隨機(jī)數(shù)和冒泡排序
package main
import "fmt"
import "time"
import "math/rand"
func main() {
fmt.Println("隨機(jī)數(shù)和冒泡排序使用演示案例")
// 1. 隨機(jī)數(shù)
rand.Seed(time.Now().UnixNano()) //以當(dāng)前系統(tǒng)時(shí)間作為種子參數(shù)
for i := 0; i < 5; i++ {
v := rand.Int() // 很大的隨機(jī)數(shù)
v = rand.Intn(100) // 100以內(nèi)的隨機(jī)數(shù)
fmt.Println("v = ", v)
}
// 2. 冒泡排序
var arr [10]int
// 隨機(jī)數(shù)初始化數(shù)組
for i := 0; i < 10; i++ {
arr[i] = rand.Intn(100)
}
fmt.Println("排序前 arr:", arr)
//冒泡排序
for i := 0; i < len(arr)-1; i++ {
for j := 0; j < len(arr)-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
fmt.Println("排序后 arr:", arr)
}
數(shù)組做函數(shù)參數(shù)和數(shù)組指針做函數(shù)參數(shù)
package main
import "fmt"
func modify(arr [10]int) {
arr[0] = 999
fmt.Println("修改中 arr:", arr)
}
func modify2(p *[10]int) {
(*p)[0] = 666 // 這里要注意一下
fmt.Println("modify2 修改中 arr:", *p)
}
func main() {
fmt.Println("隨機(jī)數(shù)和冒泡排序使用演示案例")
var arr [10]int
for i := 0; i < 10; i++ {
arr[i] = i
}
fmt.Println("修改前 arr:", arr)
modify(arr)
fmt.Println("修改后 arr:", arr)
modify2(&arr)
fmt.Println("modify2 修改后 arr:", arr)
}
切片
根據(jù)內(nèi)存和性能來看,在函數(shù)間傳遞數(shù)組是一個(gè)開銷很大的操作。在函數(shù)之間傳遞變量時(shí),總是以值的方式傳遞的。如果這個(gè)變量是一個(gè)數(shù)組,意味著整個(gè)數(shù)組,不管有多長(zhǎng),都會(huì)完整復(fù)制,并傳遞給函數(shù)。
數(shù)組的長(zhǎng)度在定義之后無法再次修改;數(shù)組是值類型,每次傳遞都將產(chǎn)生一份副本。顯然這種數(shù)據(jù)結(jié)構(gòu)無法完全滿足開發(fā)者的真實(shí)需求。Go語言提供了數(shù)組切片(slice)來彌補(bǔ)數(shù)組的不足。
切片并不是數(shù)組或數(shù)組指針,它通過內(nèi)部指針和相關(guān)屬性引?數(shù)組?段,以實(shí)現(xiàn)變??案。
slice并不是真正意義上的動(dòng)態(tài)數(shù)組,而是一個(gè)引用類型。slice總是指向一個(gè)底層array,slice的聲明也可以像array一樣,只是不需要長(zhǎng)度。
package main
import "fmt"
func main() {
fmt.Println("切片使用演示案例")
var arr1 [5]int = [5]int{1, 2, 3, 4, 5} //這是數(shù)組的定義和初始化
arr2 := [...]int{1, 2, 3, 4} // 這也是數(shù)組的定義和初始化
fmt.Printf("%T,%T\n", arr1, arr2) //[5]int,[4]int
a := []int{1, 2, 3, 4, 5} //這是切片
s := a[0:3:5] //這是切片
fmt.Printf("%T,%T\n", a, s) //[]int,[]int
fmt.Printf("cap(s):%d len(s):%d\n", cap(s), len(s)) // 5,3
s1 := a[1:3:5]
fmt.Printf("cap(s1):%d len(s1):%d\n", cap(s1), len(s1)) // 4,2
// 1. 切片的創(chuàng)建:
//slice和數(shù)組的區(qū)別:聲明數(shù)組時(shí),方括號(hào)內(nèi)寫明了數(shù)組的長(zhǎng)度或使用...自動(dòng)計(jì)算長(zhǎng)度,而聲明slice時(shí),方括號(hào)內(nèi)沒有任何字符。
// 1.1 自動(dòng)推到類型,同時(shí)初始化
ss := []int{1, 2, 3, 4} //
fmt.Println("ss=", ss) //ss= [1 2 3 4]
// 1.2 借助make函數(shù),格式:make(切片類型,長(zhǎng)度,容量)
ss2 := make([]int, 5, 8)
fmt.Println("ss2=", ss2)
// 1.3 沒有指定容量,則容量=長(zhǎng)度
ss3 := make([]int, 6) //注意:make只能創(chuàng)建slice、map和channel,并且返回一個(gè)有初始值(非零)。
fmt.Println("ss3=", ss3)
var ss4 []int //聲明切片和聲明數(shù)組差不多,只是少了長(zhǎng)度,此時(shí)為nil
ss5 := []int{}
fmt.Println(ss4, ss5) //[] []
//2. 切片截取
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("slice :", slice)
//[low:hign:max] len = hign-low cap = max-high
d1 := slice[:]
fmt.Printf("d1 len:%d,cap:%d,v:%v \n", len(d1), cap(d1), d1) //len:9,cap:9,v:[1 2 3 4 5 6 7 8 9
data := d1[3]
fmt.Println("data ", data) //4,操作某個(gè)元素和操作數(shù)組方式一樣
d2 := slice[3:6:7]
fmt.Printf("d2 len:%d,cap:%d,v:%v \n", len(d2), cap(d2), d2) //len:3,cap:4,v:[4 5 6]
d3 := slice[3:]
fmt.Printf("d3 len:%d,cap:%d,v:%v \n", len(d3), cap(d3), d3) //len:6,cap:6,v:[4 5 6 7 8 9]
d4 := slice[:6]
fmt.Printf("d4 len:%d,cap:%d,v:%v \n", len(d4), cap(d4), d4) //len:6,cap:9,v:[1 2 3 4 5 6]
// 3. 切片和底層數(shù)組關(guān)系
slice1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
z1 := slice1[2:6]
z1[1] = 666
fmt.Println(z1, slice1) //[3 666 5 6] [1 2 3 666 5 6 7 8 9]
z2 := z1[1:3] //666 5
z2[0] = 777 //z2: 777 5 z1:3 777 5 6 sl:1 2 3 777 5 6 7 8 9
fmt.Println(z1, z2, slice1) //[3 777 5 6] [777 5] [1 2 3 777 5 6 7 8 9]
// 4. append函數(shù)的使用,append函數(shù)向 slice 尾部添加數(shù)據(jù),返回新的 slice 對(duì)象
z3 := []int{}
fmt.Println(z3) //[]
z3 = append(z3, 1)
z3 = append(z3, 2) //[1 2]
fmt.Println(z3)
// 4.1 append函數(shù)會(huì)智能地底層數(shù)組的容量增長(zhǎng),一旦超過原底層數(shù)組容量,通常以2倍容量重新分配底層數(shù)組,并復(fù)制原來的數(shù)據(jù):
z4 := make([]int, 0, 1)
fmt.Printf("z4 len:%d,cap:%d,v:%v \n", len(z4), cap(z4), z4) //z4 len:0,cap:1,v:[]
for i := 0; i < 50; i++ {
z4 = append(z4, i)
fmt.Printf("len:%d,cap:%d \n", len(z4), cap(z4))
}
// 5. copy 的使用,函數(shù) copy 在兩個(gè) slice 間復(fù)制數(shù)據(jù),復(fù)制長(zhǎng)度以 len 小的為準(zhǔn),兩個(gè) slice 可指向同一底層數(shù)組。
//該函數(shù)主要是切片(slice)的拷貝,不支持?jǐn)?shù)組
//將第二個(gè)slice里的元素拷貝到第一個(gè)slice里,拷貝的長(zhǎng)度為兩個(gè)slice中長(zhǎng)度較小的長(zhǎng)度值
src := []int{1, 2, 3}
dest := []int{66, 77, 88, 99}
copy(src, dest)
fmt.Println(src, dest) //[66 77 88] [66 77 88 99]
src1 := []int{1, 2, 3}
dest1 := []int{66, 77, 88, 99}
copy(dest1, src1)
fmt.Println(src1, dest1) //[1 2 3] [1 2 3 99]
// 6. 切片作為函數(shù)參數(shù):值傳遞
slice2 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
modify(slice2)
fmt.Println(slice2) //[1 2 666 4 5 6 7 8 9]
}
func modify(s []int) {
s[2] = 666
}
Map
Go語言中的map(映射、字典)是一種內(nèi)置的數(shù)據(jù)結(jié)構(gòu),它是一個(gè)無序的key—value對(duì)的集合,比如以身份證號(hào)作為唯一鍵來標(biāo)識(shí)一個(gè)人的信息。
package main
import "fmt"
func main() {
fmt.Println("Map使用演示案例")
// 1. map的創(chuàng)建
var m1 map[int]string // 只是聲明一個(gè)map,沒有初始化,此時(shí)為nil
fmt.Println("m1 = ", m1)
// m1[1] = "java" //err,panic: assignment to entry in nil map
m2 := map[int]string{}
m3 := make(map[int]string)
m4 := make(map[int]string, 10) // 第二個(gè)參數(shù)為指定容量
m2[1] = "java"
m3[1] = "c++"
m4[1] = "go"
fmt.Println("m2 = ", m2)
fmt.Println("m3 = ", m3)
fmt.Println("m4 = ", m4)
// 2. 初始化
// 自動(dòng)類型推到
m5 := map[int]string{1: "java", 2: "c++", 3: "go"}
// 定義的時(shí)候初始化
var m6 = map[int]string{1: "go", 2: "java", 3: "c++"}
fmt.Println("m5 = ", m5)
fmt.Println("m6 = ", m6)
// 3. map的賦值
m7 := map[int]string{1: "java", 2: "c++", 3: "go"}
m7[2] = "python" //修改
m7[4] = "html" //追加
fmt.Println("m7 = ", m7)
m8 := make(map[int]string)
m8[1] = "java" //賦值
fmt.Println("m8 = ", m8)
// 4. map的遍歷
m9 := map[int]string{1: "java", 2: "c++", 3: "go"}
for k, v := range m9 {
fmt.Println("m9-->", k, v)
}
for k := range m9 { //第二個(gè)返回值value可以省略
fmt.Println("m9-->", k)
}
// 判斷某個(gè)key所對(duì)應(yīng)的value是否存在,第一個(gè)返回值是value(如果存在的話)
v, ok := m9[2]
fmt.Println("m9-->", v, ok) //m9--> c++ true
v1, ok1 := m9[4]
fmt.Println("m9-->", v1, ok1) //m9--> false
// 5. map的刪除
m10 := map[int]string{1: "java", 2: "c++", 3: "go"}
delete(m10, 2) //刪除key值為2的map
fmt.Println("m10 = ", m10)
delete(m10, 12) //刪除key值為12的map,不會(huì)報(bào)錯(cuò)
fmt.Println("m10 = ", m10)
// 6. map做函數(shù)參數(shù)為引用傳遞
m11 := map[int]string{1: "java", 2: "c++", 3: "go"}
change(m11)
fmt.Println("m11 = ", m11) //C:\Users\Ivy\Desktop\code>
}
func change(m1 map[int]string) {
m1[2] = "python"
}
結(jié)構(gòu)體
結(jié)構(gòu)體是一種聚合的數(shù)據(jù)類型,它是由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合。每個(gè)數(shù)據(jù)成為結(jié)構(gòu)體的成員
package main
import "fmt"
//定義一個(gè)結(jié)構(gòu)體類型
type Student struct {
id int
name string
age int
sex byte
addr string
}
func main() {
fmt.Println("結(jié)構(gòu)體使用演示案例")
// 1. 結(jié)構(gòu)體普通變量初始化
// 順序初始化,每個(gè)成員必須初始化
// var s1 Student = Student{1, "zz", 12, 'm'}//err, too few values in Student literal
var s1 Student = Student{1, "zz", 12, 'm', "bj"}
fmt.Println("s1 = ", s1)
// 指定成員初始化
s2 := Student{id: 2, name: "ls"} //沒有初始化的成員自動(dòng)賦值為0
fmt.Println("s2 = ", s2) //s2 = {2 ls 0 0 }
// 2. 結(jié)構(gòu)體指針變量初始化
var p1 *Student = &Student{2, "jack", 18, 'm', "sh"}
fmt.Println("p1 = ", p1) //p1 = &{2 jack 18 109 sh}
fmt.Println("&p1 = ", &p1) //&p1 = 0xc000006030
fmt.Println("*p1 = ", *p1) //*p1 = {2 jack 18 109 sh}
p2 := &Student{3, "mike", 19, 'm', "wh"}
fmt.Println("p2 = ", p2) //p2 = &{3 mike 19 109 wh}
fmt.Println("&p2 = ", &p2) //&p2 = 0xc000006038
fmt.Println("*p2 = ", *p2) //*p2 = {3 mike 19 109 wh}
// 3. 結(jié)構(gòu)成員的使用:普通變量
var s3 Student
s3.id = 3 //成員操作使用.運(yùn)算符。定義完就可以使用了,這個(gè)比較厲害
fmt.Println("s3 = ", s3) //{3 0 0 }
// 4. 結(jié)構(gòu)體成員的使用:指針變量
var s4 Student
var s5 *Student
s5 = &s4
s5.id = 5
s5.name = "cal"
(*s5).addr = "sh" // 通過指針操作成員 s5.addr 和(*s5).addr完全等價(jià)
fmt.Println("*s5 = ", *s5) //*s5 = {5 cal 0 0 sh}
// 使用new申請(qǐng)一個(gè)結(jié)構(gòu)體,先分配空間,在賦值
s6 := new(Student)
s6.name = "vi"
fmt.Println("s6 = ", s6) //s6 = &{0 vi 0 0 }
fmt.Println("*s6 = ", *s6) //*s6 = {0 vi 0 0 }
// 5.結(jié)構(gòu)體比較和賦值
//如果結(jié)構(gòu)體的全部成員都是可以比較的,那么結(jié)構(gòu)體也是可以比較的,
//那樣的話兩個(gè)結(jié)構(gòu)體將可以使用 == 或 != 運(yùn)算符進(jìn)行比較,但不支持 > 或 < 。
s7 := Student{1, "zk", 'm', 18, "bj"}
s8 := Student{1, "zk", 'm', 18, "bj"}
s9 := Student{1, "zz", 'm', 18, "bj"}
fmt.Println("s7 = s8", s7 == s8) //true
fmt.Println("s7 = s9", s7 == s9) //false
// 6. 結(jié)構(gòu)體做函數(shù)參數(shù):值傳遞
s10 := Student{1, "zk", 18, 'm', "bj"}
fmt.Println("before s10 = ", s10) //{1 zk 18 109 bj}
change(s10)
fmt.Println("after s10 = ", s10) //{1 zk 18 109 bj}
changePoint(&s10)
fmt.Println("after point s10 = ", s10) // {666 zk 18 109 bj}
}
func change(s Student) {
s.id = 666
fmt.Println("ing s = ", s)
}
func changePoint(s *Student) {
s.id = 666
fmt.Println("ing s = ", s)
}
go語言可見性規(guī)則驗(yàn)證
Go語言對(duì)關(guān)鍵字的增加非常吝嗇,其中沒有private、 protected、 public這樣的關(guān)鍵字。
要使某個(gè)符號(hào)對(duì)其他包(package)可見(即可以訪問),需要將該符號(hào)定義為以大寫字母
開頭。
如果想使用別的包的函數(shù)、結(jié)構(gòu)體類型、結(jié)構(gòu)體成員
函數(shù)名,類型名,結(jié)構(gòu)體成員變量名,首字母必須大寫,可見
如果首字母是小寫,只能在同一個(gè)包里使用
END.