### 1、下面代碼能運行嗎?為什么。
~~~go
type Param map[string]interface{}
type Show struct {
Param
}
func main1() {
s := new(Show)
s.Param["RMB"] = 10000
}
~~~
**解析**
共發現兩個問題:
1. `main`函數不能加數字。
2. `new`關鍵字無法初始化`Show`結構體中的`Param`屬性,所以直接對`s.Param`操作會出錯。
### 2、請說出下面代碼存在什么問題。
~~~go
type student struct {
Name string
}
func zhoujielun(v interface{}) {
switch msg := v.(type) {
case *student, student:
msg.Name
}
}
~~~
**解析:**
golang 中有規定,`switch type`的`case T1`,類型列表只有一個,那么`v := m.(type)`中的`v`的類型就是 T1 類型。
如果是`case T1, T2`,類型列表中有多個,那`v`的類型還是多對應接口的類型,也就是`m`的類型。
所以這里`msg`的類型還是`interface{}`,所以他沒有`Name`這個字段,編譯階段就會報錯。具體解釋見:[https://golang.org/ref/spec#Type\_switches](https://golang.org/ref/spec#Type_switches)
### 3、寫出打印的結果。
~~~go
type People struct {
name string `json:"name"`
}
func main() {
js := `{
"name":"11"
}`
var p People
err := json.Unmarshal([]byte(js), &p)
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Println("people: ", p)
}
~~~
**解析:**
按照 golang 的語法,小寫開頭的方法、屬性或`struct`是私有的,同樣,在`json`解碼或轉碼的時候也無法上線私有屬性的轉換。
題目中是無法正常得到`People`的`name`值的。而且,私有屬性`name`也不應該加`json`的標簽。
### 4、下面的代碼是有問題的,請說明原因。
~~~go
type People struct {
Name string
}
func (p *People) String() string {
return fmt.Sprintf("print: %v", p)
}
func main() {
p := &People{}
p.String()
}
~~~
**解析:**
在 golang 中`String() string`方法實際上是實現了`String`的接口的,該接口定義在`fmt/print.go`中:
~~~go
type Stringer interface {
String() string
}
~~~
在使用`fmt`包中的打印方法時,如果類型實現了這個接口,會直接調用。而題目中打印`p`的時候會直接調用`p`實現的`String()`方法,然后就產生了循環調用。
### 5、請找出下面代碼的問題所在。
~~~go
func main() {
ch := make(chan int, 1000)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
}()
go func() {
for {
a, ok := <-ch
if !ok {
fmt.Println("close")
return
}
fmt.Println("a: ", a)
}
}()
close(ch)
fmt.Println("ok")
time.Sleep(time.Second * 100)
}
~~~
**解析:**
在 golang 中`goroutine`的調度時間是不確定的,在題目中,第一個寫`channel`的`goroutine`可能還未調用,或已調用但沒有寫完時直接`close`管道,可能導致寫失敗,既然出現`panic`錯誤。
### 6、請說明下面代碼書寫是否正確。
~~~go
var value int32
func SetValue(delta int32) {
for {
v := value
if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
break
}
}
}
~~~
**解析:**
`atomic.CompareAndSwapInt32`函數不需要循環調用。
### 7、下面的程序運行后為什么會爆異常。
~~~go
type Project struct{}
func (p *Project) deferError() {
if err := recover(); err != nil {
fmt.Println("recover: ", err)
}
}
func (p *Project) exec(msgchan chan interface{}) {
for msg := range msgchan {
m := msg.(int)
fmt.Println("msg: ", m)
}
}
func (p *Project) run(msgchan chan interface{}) {
for {
defer p.deferError()
go p.exec(msgchan)
time.Sleep(time.Second * 2)
}
}
func (p *Project) Main() {
a := make(chan interface{}, 100)
go p.run(a)
go func() {
for {
a <- "1"
time.Sleep(time.Second)
}
}()
time.Sleep(time.Second * 100000000000000)
}
func main() {
p := new(Project)
p.Main()
}
~~~
**解析:**
有一下幾個問題:
1. `time.Sleep`的參數數值太大,超過了`1<<63 - 1`的限制。
2. `defer p.deferError()`需要在協程開始出調用,否則無法捕獲`panic`。
### 8、請說出下面代碼哪里寫錯了
~~~go
func main() {
abc := make(chan int, 1000)
for i := 0; i < 10; i++ {
abc <- i
}
go func() {
for a := range abc {
fmt.Println("a: ", a)
}
}()
close(abc)
fmt.Println("close")
time.Sleep(time.Second * 100)
}
~~~
**解析:**
協程可能還未啟動,管道就關閉了。
### 9、請說出下面代碼,執行時為什么會報錯
~~~go
type Student struct {
name string
}
func main() {
m := map[string]Student{"people": {"zhoujielun"}}
m["people"].name = "wuyanzu"
}
~~~
**解析:**
map 的 value 本身是不可尋址的,因為 map 中的值會在內存中移動,并且舊的指針地址在 map 改變時會變得無效。故如果需要修改 map 值,可以將`map`中的非指針類型`value`,修改為指針類型,比如使用`map[string]*Student`.
### 10、請說出下面的代碼存在什么問題?
~~~go
type query func(string) string
func exec(name string, vs ...query) string {
ch := make(chan string)
fn := func(i int) {
ch <- vs[i](name)
}
for i, _ := range vs {
go fn(i)
}
return <-ch
}
func main() {
ret := exec("111", func(n string) string {
return n + "func1"
}, func(n string) string {
return n + "func2"
}, func(n string) string {
return n + "func3"
}, func(n string) string {
return n + "func4"
})
fmt.Println(ret)
}
~~~
**解析:**
依據 4 個 goroutine 的啟動后執行效率,很可能打印 111func4,但其他的 111func\*也可能先執行,exec 只會返回一條信息。
### 11、下面這段代碼為什么會卡死?
~~~go
package main
import (
"fmt"
"runtime"
)
func main() {
var i byte
go func() {
for i = 0; i <= 255; i++ {
}
}()
fmt.Println("Dropping mic")
// Yield execution to force executing other goroutines
runtime.Gosched()
runtime.GC()
fmt.Println("Done")
}
~~~
**解析:**
Golang 中,byte 其實被 alias 到 uint8 上了。所以上面的 for 循環會始終成立,因為 i++ 到 i=255 的時候會溢出,i <= 255 一定成立。
也即是, for 循環永遠無法退出,所以上面的代碼其實可以等價于這樣:
~~~go
go func() {
for {}
}
~~~
正在被執行的 goroutine 發生以下情況時讓出當前 goroutine 的執行權,并調度后面的 goroutine 執行:
* IO 操作
* Channel 阻塞
* system call
* 運行較長時間
如果一個 goroutine 執行時間太長,scheduler 會在其 G 對象上打上一個標志( preempt),當這個 goroutine 內部發生函數調用的時候,會先主動檢查這個標志,如果為 true 則會讓出執行權。
main 函數里啟動的 goroutine 其實是一個沒有 IO 阻塞、沒有 Channel 阻塞、沒有 system call、沒有函數調用的死循環。
也就是,它無法主動讓出自己的執行權,即使已經執行很長時間,scheduler 已經標志了 preempt。
而 golang 的 GC 動作是需要所有正在運行`goroutine`都停止后進行的。因此,程序會卡在`runtime.GC()`等待所有協程退出。
- Golnag常見面試題目解析
- 交替打印數組和字母
- 判斷字符串中字符是否全都不同
- 翻轉字符串
- 判斷兩個給定的字符串排序后是否一致
- 字符串替換問題
- 機器人坐標計算
- 語法題目一
- 語法題目二
- goroutine和channel使用一
- 實現阻塞讀的并發安全Map
- 定時與 panic 恢復
- 高并發下的鎖與map讀寫問題
- 為 sync.WaitGroup 中Wait函數支持 WaitTimeout 功能.
- 七道語法找錯題目
- golang 并發題目測試
- 記一道字節跳動的算法面試題
- 多協程查詢切片問題
- 對已經關閉的的chan進行讀寫,會怎么樣?為什么?
- 簡單聊聊內存逃逸?
- 字符串轉成byte數組,會發生內存拷貝嗎?
- http包的內存泄漏