golang學習難點和易錯點

2021-10-08 01:28:03 字數 3342 閱讀 5704

0.要想寫出能正確執行的golang**,有幾隻怪獸需要打倒(我稱之為golang feature):slice, inte***ce, panic/recover/defer, channel, goroutine, reflection, package import/management等等...後面我會詳細記錄打倒這些怪獸的過程,先挖坑...

0.在深入理解slice之前,先回憶一下golang的陣列,一般形式是a := [3]int,陣列邏輯上一片連續的記憶體,因此陣列由指向其第乙個元素的指標ptr,陣列的長度(所有已經初始化的元素數目,使用len(a)得到)和陣列的大小(golang引入的概念,使用cap(a)得到)決定;由於golang的陣列的每個元素都是初始化好的,因此len(a)一定等於陣列的大小,也即cap(a), 因此,對於陣列,我們一般只關心其長度,對陣列的操作(查詢或者修改元素值)一定限定在長度範圍內,否則會報runtime error: index out of range;

1.說到slice,一般形式是s := int, 與陣列相比就是無需指定其大小(方括號中間不要加數字),這種初始化方式等價於:

s := make(int, 3)
來看一種通用的slice初始化方式:

s := make(int, 5, 10) // 表示len(a) == 5, cap(a) == 10
,否則會報runtime error: index out of range

2.對slice進行切片,也即得到slice的slice,看看下面的**:

package main

func main()

// 執行結果為:5 15

結合**和執行結果,可知對slice進行切片,其返回的slice的指標將指向切口的起始元素,並且從指標位置開始,一直復用s後續的記憶體空間(也叫底層陣列);也即b的指標是指向了s的第6個元素(s[5]),同時b復用s的初始化元素及其未初始化的元素,此時s長度為10(初始化了前10個元素), 大小為20,那麼從s[6]算起,有5個元素被初始化,加上未初始化的10個元素,一共是15個元素的大小,因此len(b) == 5, cap(b) == 15

,那麼以下的**也不足為道:

func strangeslice()
並且對下面的執行結果能說出個所以然來:

// 執行結果

&a: 0xc000024140, len: 1, cap: 10, a: [0]

&b: 0xc000024140, len: 2, cap: 10, b: [0 1]

&b: 0xc000024140, len: 2, cap: 10, b: [0 2]

4.個人認為比較好的slice使用方式:

// slice有具體的初始化值

s := int

// slice沒有具體的初始化值

s := int{} // 或者var s int

5.slice之間元素的複製,即把源slice的元素複製到目標slice中,可以使用copy(d, s)函式

func pracopy() 

d := make(int, 2, 5)

copy(d, s)

// 當且僅當len(d) > len(s)時copy函式等價於以下**

/*for i := range s

*/fmt.printf("&d: %p, len: %d, cap: %d, d: %v\n", d, len(d), cap(d), d)

}

copy函式只會把目標slice中已初始化的元素修改為源slice中對應的元素值,而未初始化的元素不會被修改,依舊保持未初始化狀態;對於上面的**,由於d的長度為2(也即初始化了2個元素),那麼即使s的長度為3,也只會把s的前2個元素複製到d中,因此執行結果為:

&d: 0xc00001e2d0, len: 2, cap: 5, d: [1 2]
相關參考:golang slice詳解,go語言slice的那些坑

func strangerange() 

for i := range v

fmt.println(v)

}

1.range的用法中,比較容易出錯的就是在遍歷多元素資料型別的同時要修改其中的內容,

2.range迴圈在內部實現上實際就是 c 風格迴圈的語法糖,遍歷到的值會被賦值給乙個臨時變數,再來一段**:

func strangerange() 

// 以下**遍歷陣列和遍歷slice結果不一樣

for i, v := range a

a[i] = v + 100

} fmt.println(a)

}

相關參考:go range 內部實現 [譯]

0.首先要記住:go語言中所有的傳參都是值傳遞(傳值),都是乙個副本,乙個拷貝(所以要特別留意記憶體複製的問題!!!)。因為拷貝的內容有時候是非引用型別(int、string、struct等),這樣就在函式中就無法修改原內容資料;有的是引用型別(指標、map、slice、chan等),這樣就可以修改原內容資料

1.將乙個陣列型別變數賦值a給另乙個陣列型別b,則產生一次記憶體複製問題:

func praassignment() 

b := a // a為陣列時會發生記憶體拷貝, a和b的內容相同,但是記憶體位址不同!!!

fmt.printf("[before]&a: %p,a: %v; &b: %p, b: %v\n", &a[0], a, &b[0], b)

b[0] = 6

fmt.printf("[after]&a: %p,a: %v; &b: %p, b: %v\n", &a, a, &b, b)

}

這種寫法應該盡量避免,但如果a的型別為slice,則不會有記憶體複製問題(此時的賦值形式和python的陣列類似)

具體可以參考:go語言引數傳遞是傳值還是傳引用

先參考:拜拜了,gopath君!新版本golang的包管理入門教程

先參考:理解go語言的nil,理解go nil

先參考:golang的反射reflect深入理解和示例

Android總結和易錯點

一 開發環境 1 安裝jdk 2 設定環境變數 新建乙個系統環境變數,變數名為android sdk home,變數值為你的sdk安裝路徑 把 android sdk home platform tools android sdk home tools新增到path環境變數中。注意分號 andrio...

指標難點 易錯點

voidf char c intmain f c 這裡的f char c 指向指標的指標 所以呼叫時一定要 使用指標 c 3 定義指標陣列 c 第乙個指標變數的位址。指標的位址 用指向指標的指標存放 二 例如 int a 3 4 ptr2是乙個指向 int 的指標,即ptr2的型別和 ptr是一樣的...

C語言易錯點及難點

獲得字串長度 strlen s 需要標頭檔案 include 判斷兩個字元是否相等用 判斷兩個字串相等 if strcmp s1,s2 0 strcmp s1,s2 相等返回0 想要輸入帶空格用gets s 標頭檔案stdlib.h中的方法atoi可以實現字串轉數字 996 轉為996 includ...