go學(xué)習(xí)筆記

1.for range結(jié)合指針
如下寫法輸出的*v都是m的最后一個(gè)value
for k,v := range m {
fmt.Println(k,"->",*v)
}
解法:每次循環(huán)重新賦新的變量,取新變量的地址
for key,val := range slice {
value := val
m[key] = &value
}
注意:value變量的生命周期是單次循環(huán)結(jié)束,因?yàn)橄麓窝h(huán)就被重新定義了(:=)

2.new() 與 make() 的區(qū)別
參考答案:
new(T) 和 make(T,args) 是 Go 語言內(nèi)建函數(shù),用來分配內(nèi)存,但適用的類型不同。
new(T) 會為 T 類型的新值分配已置零的內(nèi)存空間,并返回地址(指針),即類型為 *T的值。換句話說就是,返回一個(gè)指針,該指針指向新分配的、類型為 T 的零值。適用于值類型,如數(shù)組、結(jié)構(gòu)體等。
make(T,args) 返回初始化之后的 T 類型的值,這個(gè)值并不是 T 類型的零值,也不是指針 *T,是經(jīng)過初始化之后的 T 的引用。make() 只適用于 slice、map 和 channel.

3.append追加slice
append() 的第二個(gè)參數(shù)不能直接使用 slice,需使用 … 操作符,將一個(gè)切片追加到另一個(gè)切片上:append(s1,s2…)。或者直接跟上元素,形如:append(s1,1,2,3)

4.結(jié)構(gòu)體比較
(1)結(jié)構(gòu)體只能比較是否相等,但是不能比較大小。
(2)相同類型的結(jié)構(gòu)體才能夠進(jìn)行比較,結(jié)構(gòu)體是否相同不但與屬性類型有關(guān),還與屬性順序相關(guān),比如
struct {
name string
age int
}{age:11,name:"qq"}
和struct {
age int
name string
}{age:11,name:"qq"}不相等
(3)如果 struct 的所有成員都可以比較,則該 struct 就可以通過 == 或 != 進(jìn)行比較是否相等,比較時(shí)逐個(gè)項(xiàng)進(jìn)行比較,如果每一項(xiàng)都相等,則兩個(gè)結(jié)構(gòu)體才相等,否則不相等。那么什么類型可以比較,什么類型不可比較呢?bool、數(shù)值型、字符、指針、數(shù)組等可以比較,但是像切片、map、函數(shù)等是不能比較的。

5.類型別名
type MyInt1 int //定義一個(gè)新類型,名為MyInt1
type MyInt2 = int //為int類型定義別名MyInt2,實(shí)質(zhì)還是int
var i1 MyInt1 = i //go是強(qiáng)類型語言,類型不一致,報(bào)錯(cuò)
var i2 MyInt2 = i //正常賦值
特殊:
type T []int
func F(t T) {}
func main() {
var q []int
F(q)
}
我們知道不同類型的值是不能相互賦值的,即使底層類型一樣,所以上面編譯不通過;但是對于底層類型相同的變量可以相互賦值還有一個(gè)重要的條件,即至少有一個(gè)不是有名類型(named type)。
這是 Go 語言規(guī)范手冊的原文:
"x's type V and T have identical underlying types and at least one of V or T is not a named type. "
Named Type 有兩類:
1.內(nèi)置類型,比如 int, int64, float, string, bool 等;
2.使用關(guān)鍵字 type 聲明的類型;
Unnamed Type 是基于已有的 Named Type 組合一起的類型,例如:struct{}、[]string、interface{}、map[string]bool 等。

6.字符串連接方式
(1)str := “abc” + “123”
(2)fmt.Sprintf(“abc%d”, 123)
(3)strings.Join()

s1 := []string{"111", "222"}
s2 := strings.Join(s1, "www")
fmt.Println(s2) //111www222

(4)buffer.WriteString()

7.init說明
init() 函數(shù)是用于程序執(zhí)行前做包的初始化的函數(shù),比如初始化包里的變量等;
一個(gè)包可以出線多個(gè) init() 函數(shù),一個(gè)源文件也可以包含多個(gè) init() 函數(shù);
同一個(gè)包中多個(gè) init() 函數(shù)的執(zhí)行順序沒有明確定義,但是不同包的init函數(shù)是根據(jù)包導(dǎo)入的依賴關(guān)系決定的(看下圖);
init() 函數(shù)在代碼中不能被顯示調(diào)用、不能被引用(賦值給函數(shù)變量),否則出現(xiàn)編譯錯(cuò)誤;
一個(gè)包被引用多次,如 A import B,C import B,A import C,B 被引用多次,但 B 包只會初始化一次;
引入包,不可出現(xiàn)死循壞。即 A import B,B import A,這種情況編譯失??;

8.類型斷言
語法形如:i.(type),其中 i 是接口,type 是固定關(guān)鍵字,需要注意的是,只有接口類型才可以使用類型選擇

9.切片是指向數(shù)組的指針,且不能比較
func hello(num ...int) {
num[0] = 18
}
func main() {
i := []int{5, 6, 7}
hello(i...)
fmt.Println(i[0]) //輸出18,因?yàn)槭褂玫氖乔衅?,傳遞的是地址,如果換成數(shù)組則輸出5
}
并且,切片是不能比較的!

10.截取操作符 [ i:j ]
a.截取操作符還可以有第三個(gè)參數(shù),形如 [i:j:k],第三個(gè)參數(shù) k 用來限制新切片的容量,但不能超過原數(shù)組(切片)的底層數(shù)組大小。截取獲得的切片的長度和容量分別是:j-i、k-i。
b. [ i: ]默認(rèn)取原slice的len大小,相當(dāng)于 [ i:len(slice) ]
x := make([]int, 2, 10)
c := x[6:] // panic,因?yàn)槟J(rèn)6:默認(rèn)取6:2,6>2導(dǎo)致panic
c := x[2:] // [],2:2相當(dāng)于沒有取

11.數(shù)組比較
Go 中的數(shù)組是值類型,可比較,但是,數(shù)組的長度也是數(shù)組類型的組成部分,所以 [2]int{5,6} 和 [3]int{5,6} 是不同的類型,是不能比較的,會產(chǎn)生編譯錯(cuò)誤。

12.cap()
cap() 函數(shù)適用array,slice,channel,但是不適用 map

13.map
func main() {
s := make(map[string]int)
delete(s, "h") //不報(bào)錯(cuò)
fmt.Println(s["h"]) //0
}
刪除 map 不存在的鍵值對時(shí),不會報(bào)錯(cuò),相當(dāng)于沒有任何作用;獲取不存在的減值對時(shí),返回值類型對應(yīng)的零值,所以返回 0。

14.map
%d表示輸出十進(jìn)制數(shù)字,%+d表示輸出數(shù)值的符號。

15.defer 的原理
一文讓你徹底明白 defer 的原理 - 古明地盆 - 博客園 (cnblogs.com)

16.接口,切片實(shí)質(zhì)都是指針
當(dāng)且僅當(dāng)動態(tài)值和動態(tài)類型都為 nil 時(shí),接口類型值才為 nil。動態(tài)類型是賦給接口的類型,動態(tài)值是 賦給接口的類型變量的值。

17.map中的struct不可尋址
var m = map[string]Math{
"foo": Math{2, 3},
}
m["foo"].x = 4 //cannot assign to struct field m[“foo”].x in map
原因:map中的struct不可尋址
解決:使用指針
var m = map[string]*Math{
"foo": &Math{2, 3},
}
m["foo"].x = 4

18.defer panic return執(zhí)行順序
func f(n int) (r int) {
defer func() {
r += n
recover()
}()
var f func()
defer f()
f = func() {
r += 2
}
return n + 1
}
func main() {
fmt.Println(f(3))
}
第一步執(zhí)行r = n +1,接著執(zhí)行第二個(gè) defer,由于此時(shí) f() 未定義,引發(fā)異常,隨即執(zhí)行第一個(gè) defer,異常被 recover(),程序正常執(zhí)行,最后 return,輸出7。

19.切片len超出cap的時(shí)候會擴(kuò)容,但是底層數(shù)組本身是不能擴(kuò)容的,所以指向的底層數(shù)組會變成一個(gè)新數(shù)組
func change(s ...int) {
s = append(s,3)
}
func main() {
slice := make([]int,5,5)
slice[0] = 1
slice[1] = 2
change(slice...)
fmt.Println(slice)
change(slice[0:2]...)
fmt.Println(slice)
}
輸出結(jié)果為:
[1 2 0 0 0]
[1 2 3 0 0]
知識點(diǎn):可變函數(shù)、append()操作。Go 提供的語法糖…,可以將 slice 傳進(jìn)可變函數(shù),不會創(chuàng)建新的切片。第一次調(diào)用 change() 時(shí),append() 操作使切片底層數(shù)組發(fā)生了擴(kuò)容,原 slice 的底層數(shù)組不會改變;第二次調(diào)用change() 函數(shù)時(shí),使用了操作符[i,j]獲得一個(gè)新的切片,假定為 slice1,它的底層數(shù)組和原切片底層數(shù)組是重合的,不過 slice1 的長度、容量分別是 2、5,所以在 change() 函數(shù)中對 slice1 底層數(shù)組的修改會影響到原切片。
練習(xí)題:
func main() {
a := [3]int{0, 1, 2}
s := a[1:2]
s[0] = 11
s = append(s, 12)
s = append(s, 13)
s[0] = 21
fmt.Println(a)
fmt.Println(s)
}
輸出:
[0 11 12]
[21 12 13]

20.break高級用法:指定退出哪層循環(huán)
I:
for i := 0; i < 2; i++ {
for j := 0; j < 5; j++ {
if j == 2 {
break I
}
fmt.Println("hello")
}
fmt.Println("hi")
}
其中,I是自定義的名字,可以隨意

21.switch單個(gè)case中,可以出現(xiàn)多個(gè)結(jié)果選項(xiàng)
以逗號隔開,如case 1,2: 代表1或2都可以

22.channel知識
關(guān)于channel的特性,下面說法正確的是?
A. 給一個(gè) nil channel 發(fā)送數(shù)據(jù),造成永遠(yuǎn)阻塞
B. 從一個(gè) nil channel 接收數(shù)據(jù),造成永遠(yuǎn)阻塞
C. 給一個(gè)已經(jīng)關(guān)閉的 channel 發(fā)送數(shù)據(jù),引起 panic
D. 從一個(gè)已經(jīng)關(guān)閉的 channel 接收數(shù)據(jù),如果緩沖區(qū)中為空,則返回一個(gè)零值
參考答案及解析:ABCD。
有方向的 channel 不可以被關(guān)閉。

23.常量知識
a.常量不同于變量的在運(yùn)行期分配內(nèi)存,常量通常會被編譯器在預(yù)處理階段直接展開,作為指令數(shù)據(jù)使用,所以常量無法尋址。
b.不像變量,常量未使用是能編譯通過的。
c.常量組中如不指定類型和初始化值,則與上一行常量右值相同
const (
x uint16 = 120
y // 與x一致
s = "abc"
z // 與s一致
)

24.下面這段代碼存在什么問題?
type Param map[string]interface{}
type Show struct {
*Param
}
func main() {
s := new(Show)
s.Param["day"] = 2
}
參考答案及解析:存在兩個(gè)問題:1.map 需要初始化才能使用;2.指針不支持索引。修復(fù)代碼如下:
func main() {
s := new(Show)
// 修復(fù)代碼
p := make(Param)
p["day"] = 2
s.Param = &p
tmp := *s.Param
fmt.Println(tmp["day"])
}

25.切片定義時(shí)的索引
var x = []int{2: 2, 3, 0: 1}
fmt.Println(x)
輸出[1 0 2 3],字面量初始化切片時(shí)候,可以指定索引,沒有指定索引的元素會在前一個(gè)索引基礎(chǔ)之上加一,所以輸出[1 0 2 3]。

26.作用域{}
func main() {
?x := 1
?fmt.Println(x)
?{
? ?fmt.Println(x)
? ?i,x := 2,2
??fmt.Println(i,x)
?}
?fmt.Println(x) // print ?
}
參考答案及解析:輸出1。知識點(diǎn):變量隱藏。使用變量簡短聲明符號 := 時(shí),如果符號左邊有多個(gè)變量,只需要保證至少有一個(gè)變量是新聲明的,并對已定義的變量盡進(jìn)行賦值操作。但如果出現(xiàn)作用域之后,就會導(dǎo)致變量隱藏的問題,就像這個(gè)例子一樣。

26.json.Marshal()
結(jié)構(gòu)體生成json文本成員變量必須大寫,比如
type People struct {
Name string `json:"name"`
}

27.switch的fallthrough
switch(i) {
?case 1:
??fallthrough //不管case2條件滿不滿足,都進(jìn)入執(zhí)行case2
?case 2:
??return true
}

28.recover()
recover() 必須在 defer 定義的函數(shù)內(nèi)部調(diào)用才有效
defer func() {
?recover()
}()

29.for range
下面的代碼輸出什么?

type T struct {
    n int
}

func main() {
    ts := [2]T{}
    for i, t := range &ts {
        switch i {
        case 0:
            t.n = 3
            ts[1].n = 9
        case 1:
            fmt.Print(t.n, " ")
        }
    }
    fmt.Print(ts)
}

參考答案及解析:9 [{0} {9}]。知識點(diǎn):for-range 數(shù)組指針。for-range 循環(huán)中的循環(huán)變量 t 是原數(shù)組元素的副本。如果數(shù)組元素是結(jié)構(gòu)體值,則副本的字段和原數(shù)組字段是兩個(gè)不同的值。

30.map
map在賦值前要make來分配內(nèi)存
map 反序列化時(shí) json.unmarshal() 的入?yún)⒈仨殲閙ap的地址
map 是并發(fā)讀寫不安全的

31.可變函數(shù)
可變函數(shù)是指針傳遞
func hello(num ...int) {
num[0] = 18
}
i := []int{5, 6, 7}
hello(i...)

fmt.Println(i[0]) // 18

30.Unnamed Type 不能作為方法的接收者

func (m map[string]string) Set(key string, value string) { // 修復(fù):先type User  map[string]string , func定義接收者為m User即可
    m[key] = value
}

func main() {
    m := make(map[string]string)
    m.Set("A", "One")
}

31.sync.WaitGroup
WaitGroup 在調(diào)用 Wait() 之后不能再調(diào)用 Add() 方法
go func里面不能定義sync.WaitGroup

32.鎖機(jī)制
sync.Mutex
RWMutex

申明:本文內(nèi)容提煉自http://www.topgoer.cn/docs/gomianshiti,并在自己學(xué)習(xí)過程中加以理解和總結(jié),感謝原作者提供很好的學(xué)習(xí)渠道,萬分感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • fmt格式化字符串 格式:%[旗標(biāo)][寬度][.精度][arg索引]動詞旗標(biāo)有以下幾種:+: 對于數(shù)值類型總是輸出...
    皮皮v閱讀 1,223評論 0 3
  • 1、賦值規(guī)則 賦值可以看作是隱式類型轉(zhuǎn)換 賦值語句中的目標(biāo)值必須為一個(gè)可尋址的值、一個(gè)映射元素表達(dá)式或者一個(gè)空標(biāo)識...
    陳德華閱讀 622評論 0 0
  • 創(chuàng)建字符串 var str = "Hello world!" str := "Hello world!" 字符串長...
    小流歌_閱讀 266評論 0 0
  • 能力模式 選擇題 【初級】下面屬于關(guān)鍵字的是()A. funcB. defC. structD. class 參考...
    靈魂深靈閱讀 5,387評論 2 5
  • 整理自golang中文網(wǎng)https://studygolang.com/ 以及公眾號 golang來啦 (侵刪)...
    p_gerer閱讀 1,909評論 1 2

友情鏈接更多精彩內(nèi)容