Open mangreen opened 2 years ago
https://kitecloud-backend.coderbridge.io/2020/08/15/golang-slice-%E4%BD%9C%E7%82%BA%E5%8F%83%E6%95%B8%E5%82%B3%E9%81%9E%E6%99%82%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85/ Golang 傳遞參數進 func ,當 pass by value 時,會在進入 func 時複製一份。
func main() { ary := [1]int{}
fmt.Println("main", ary, fmt.Sprintf("%p", &ary))
test(ary)
fmt.Println("main", ary, fmt.Sprintf("%p", &ary))
}
func test(a [1]int) { a[0] = 1 fmt.Println("test", a, fmt.Sprintf("%p", &a)) } / main [0] 0xc000018030 test [1] 0xc000018038 main [0] 0xc000018030 /
可以看到傳入 func 的 array 記憶體位址改變了
同樣的情況放到 slice 就稍微有點不同:
```golang
func main() {
slice := []int{}
fmt.Println("main", slice, fmt.Sprintf("%p", &slice))
test(slice)
fmt.Println("main", slice, fmt.Sprintf("%p", &slice))
}
func test(s []int) {
s[0] = 1
fmt.Println("test", s, fmt.Sprintf("%p", &s))
}
/*
main [0] 0xc00000c030
test [1] 0xc00000c048
main [1] 0xc00000c030
*/
一樣可以發現傳進去的 slice 記憶體位址改變了, 但最後把 slice 內容印出來顯示 slice[0] 有正確改變成 1, 這是因為 slice 的資料其實不是儲存在它自己本身上的。 所以看起來很像 pass by value 可以修改到同一個 slice, 實際上還是在操作副本。
雖然副本 slice 可以操作同一個 array, 但如果是使用 append() 這種會改變 slice len 的操作, 就只會改到副本 slice 而不是原先的 slice,
func main() {
slice := []int{0}
fmt.Println("main", slice, fmt.Sprintf("%p", &slice))
test(slice)
fmt.Println("main", slice, fmt.Sprintf("%p", &slice))
}
func test(s []int) {
s = append(s, 1)
fmt.Println("test", s, fmt.Sprintf("%p", &s))
}
/*
main [0] 0xc00000c030
test [0 1] 0xc00000c060
main [0] 0xc00000c030
*/
slice 還沒超過 cap 時,你操作 slice 等於影響原始資料 array; 當 slice 持續 append 導致 cap 超過 array 長度時, 會自動幫你生成一個 2倍大的 array 、複製資料過去、並把 slice 指向這個新的 slice。此時你操作 slice 影響到的不是原本的 array 而是 2倍大的 array 所以在使用 slice 時要注意是否超過 cap 導致生成新的 array, 常見的陷阱是資料小於 cap 時可以正常透過 slice 修改 array, 一旦超過 cap 就會發現怎麼修改都無法改變原始的 array。
https://pjchender.dev/golang/pointers/
func main() {
q := new(hp)
q.name = "kevin"
fmt.Println(q.name) // kevin
q.rename1("ben")
fmt.Println(q.name) // kevin
q.rename2("ben")
fmt.Println(q.name) // ben
}
type hp struct {
name string
}
func (h hp) rename1(n string) {
h.name = n
}
func (h *hp) rename2(n string) {
h.name = n
}
func main() {
q1 := new(hp)
fmt.Println(unsafe.Sizeof(q1)) // 8
q2 := hp{}
fmt.Println(unsafe.Sizeof(q2)) // 16
name := ""
fmt.Println(unsafe.Sizeof(name)) // 16
q3 := &hp{}
fmt.Println(unsafe.Sizeof(q3)) // 8
}
type hp struct {
name string
}
https://juejin.cn/post/7033671944587182087
傳送時關閉
// 1.未初始化時關閉
func TestCloseNilChan(t *testing.T) {
var errCh chan error
close(errCh)
// Output:
// panic: close of nil channel
}
// 2.重複關閉 func TestRepeatClosingChan(t *testing.T) { errCh := make(chan error) var wg sync.WaitGroup wg.Add(1)
go func() { defer wg.Done() close(errCh) close(errCh) }()
wg.Wait()
// Output: // panic: close of closed channel }
// 3.關閉後傳送 func TestSendOnClosingChan(t *testing.T) { errCh := make(chan error) var wg sync.WaitGroup wg.Add(1)
go func() { defer wg.Done() close(errCh) errCh <- errors.New("chan error") }()
wg.Wait()
// Output: // panic: send on closed channel }
// 4.傳送時關閉 func TestCloseOnSendingToChan(t *testing.T) { errCh := make(chan error) var wg sync.WaitGroup wg.Add(1)
go func() { defer wg.Done() defer close(errCh)
go func() {
errCh <- errors.New("chan error") // 由於 chan 沒有緩衝佇列,程式碼會一直在此處阻塞
}()
time.Sleep(time.Second) // 等待向 errCh 傳送資料
}()
wg.Wait()
// Output: // panic: send on closed channel }
## Q: go channel 關閉後,讀取該 channel 會?
### A: 永遠不會阻塞,且只會輸出對應類型的零值。
```golang
func TestReadFromClosedChan(t *testing.T) {
var errCh = make(chan error)
go func() {
defer close(errCh)
errCh <- errors.New("chan error")
}()
go func() {
for i := 0; i < 3; i++ {
fmt.Println(i, <-errCh)
}
}()
time.Sleep(time.Second)
// Output:
// 0 chan error
// 1 <nil>
// 2 <nil>
}
https://blog.csdn.net/shida_csdn/article/details/88344017
多個 goroutine 等待讀取 ch,雖然誰先呼叫讀取是不確定的, 但是,誰先呼叫了讀取並被阻塞,誰就能在通道資料可用時先被喚醒
https://juejin.im/post/6844903917650722829 https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/752833/#outline__2
一次GC有兩次觸發STW,一次是GC的開始階段,主要是開啟寫屏障和輔助GC等操作 另外就是表記完成之後,重新掃描部分根對象,停用寫屏障
https://cloud.tencent.com/developer/article/1900650 https://anakinsun.com/post/golang-gc/
在手動觸發的場景下,Go 語言中僅有 runtime.GC 方法可以觸發,也就沒什麼額外的分類的。
defer 是延遲執行,可以將其後面的程式碼延後至函式結束時執行。Defer 可以 stack,執行順序先進後出
func main() {
fmt.Println("defer begin")
defer fmt.Println("Hello World")
defer fmt.Println("Hey World")
defer fmt.Println("Hi World")
fmt.Println("defer end")
}
/* print out:
defer begin
defer end
Hi World
Hey World
Hello World
*/
recover 可捕捉系統自動產生或手動設定產生的 panic 錯誤,回復系統以避免程序崩潰。recover 必須和 defer 配合使用!recover 必須和 defer 配合使用!recover 必須和 defer 配合使用!(很重要)
package main
import "fmt"
func main() {
fmt.Println("Taiwan: Here Comes 2021")
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
fmt.Println("Don't panic! We got vaccinated!")
}
fmt.Println("Life still moves on!")
}()
panic("Oh! No! COVID Pandemic!")
}
https://studygolang.com/articles/23191?utm_source=tuicool&utm_medium=referral
https://my.oschina.net/aom/blog/4279175 理解 M: Machine、P: Processor、G:Goroutine 三者的關係,可以通過經典的地鼠推車搬磚的模型來說明其三者關係: 地鼠(Gopher)的工作任務是:工地上有若干磚頭,地鼠借助小車把磚頭運送到火種上去燒製。M 就可以看作圖中的地鼠,P 就是小車,G 就是小車裡裝的磚。
golang面試題整理
slice & channel 的底层原理和注意事项
常規性Golang面試題解析
Go 實現常用設計模式
Go 易錯面試題彙總 (code)
type assertion
sync
匿名函數語閉包