golang面試題解析

來源:互聯網
上載者:User

最近在很多地方看到了golang的面試題,看到了很多人對Golang的面試題心存恐懼,也是為了複習基礎,我把解題的過程總結下來。

面試題

1. 寫出下面代碼輸出內容。

package mainimport (    "fmt")func main() {    defer_call()}func defer_call() {    defer func() { fmt.Println("列印前") }()    defer func() { fmt.Println("列印中") }()    defer func() { fmt.Println("列印後") }()    panic("觸發異常")}

考點:defer執行順序
解答: defer 是後進先出
panic 需要等defer 結束後才會向上傳遞。出現panic恐慌時候,會先按照defer的後入先出的順序執行,最後才會執行panic。

列印後列印中列印前panic: 觸發異常

2. 以下代碼有什麼問題,說明原因。

type student struct {    Name string    Age  int}func pase_student() {    m := make(map[string]*student)    stus := []student{        {Name: "zhou", Age: 24},        {Name: "li", Age: 23},        {Name: "wang", Age: 22},    }    for _, stu := range stus {        m[stu.Name] = &stu    }}

考點:foreach
解答:這樣的寫法初學者經常會遇到的,很危險!與Java的foreach一樣,都是使用副本的方式。所以m[stu.Name]=&stu實際上一致指向同一個指標,最終該指標的值為遍曆的最後一個struct的值拷貝。就像想修改切片元素的屬性:

for _, stu := range stus {    stu.Age = stu.Age+10}

也是不可行的。大家可以試試列印出來:

func pase_student() {    m := make(map[string]*student)    stus := []student{        {Name: "zhou", Age: 24},        {Name: "li", Age: 23},        {Name: "wang", Age: 22},    }    // 錯誤寫法    for _, stu := range stus {        m[stu.Name] = &stu    }    for k,v:=range m{        println(k,"=>",v.Name)    }    // 正確    for i:=0;i<len(stus);i++  {        m[stus[i].Name] = &stus[i]    }    for k,v:=range m{        println(k,"=>",v.Name)    }}

3. 下面的代碼會輸出什麼,並說明原因

func main() {    runtime.GOMAXPROCS(1)    wg := sync.WaitGroup{}    wg.Add(20)    for i := 0; i < 10; i++ {        go func() {            fmt.Println("A: ", i)            wg.Done()        }()    }    for i := 0; i < 10; i++ {        go func(i int) {            fmt.Println("B: ", i)            wg.Done()        }(i)    }    wg.Wait()}

考點:go執行的隨機性和閉包
解答:誰也不知道執行後列印的順序是什麼樣的,所以只能說是隨機數字。但是A:均為輸出10,B:從0~9輸出(順序不定)。第一個go func中i是外部for的一個變數,地址不變化。遍曆完成後,最終i=10。故go func執行時,i的值始終是10。

第二個go func中i是函數參數,與外部for中的i完全是兩個變數。尾部(i)將發生值拷貝,go func內部指向值拷貝地址。

4. 下面代碼會輸出什嗎?

type People struct{}func (p *People) ShowA() {    fmt.Println("showA")    p.ShowB()}func (p *People) ShowB() {    fmt.Println("showB")}type Teacher struct {    People}func (t *Teacher) ShowB() {    fmt.Println("teacher showB")}func main() {    t := Teacher{}    t.ShowA()}

考點:go的組合繼承
解答:這是Golang的組合模式,可以實現OOP的繼承。被組合的類型People所包含的方法雖然升級成了外部類型Teacher這個組合類別型的方法(一定要是匿名欄位),但它們的方法(ShowA())調用時接受者並沒有發生變化。此時People類型並不知道自己會被什麼類型組合,當然也就無法調用方法時去使用未知的組合者Teacher類型的功能。

showAshowB

5. 下面代碼會觸發異常嗎?請詳細說明

func main() {    runtime.GOMAXPROCS(1)    int_chan := make(chan int, 1)    string_chan := make(chan string, 1)    int_chan <- 1    string_chan <- "hello"    select {    case value := <-int_chan:        fmt.Println(value)    case value := <-string_chan:        panic(value)    }}

考點:select隨機性
解答: select會隨機播放一個可用通用做收發操作。所以代碼是有肯觸發異常,也有可能不會。單個chan如果無緩衝時,將會阻塞。但結合 select可以在多個chan間等待執行。有三點原則: 

  •  select 中只要有一個case能return,則立刻執行。 *

  • 當如果同一時間有多個case均能return則偽隨機方式抽取任意一個執行。

  •  如果沒有一個case能return則可以執行”default”塊。

6. 下面代碼輸出什嗎?

func calc(index string, a, b int) int {    ret := a + b    fmt.Println(index, a, b, ret)    return ret}func main() {    a := 1    b := 2    defer calc("1", a, calc("10", a, b))    a = 0    defer calc("2", a, calc("20", a, b))    b = 1}

考點:defer執行順序
解答:這道題類似第1題需要注意到defer執行順序和值傳遞 index:1肯定是最後執行的,但是index:1的第三個參數是一個函數,所以最先被調用calc("10",1,2)==>10,1,2,3 執行index:2時,與之前一樣,需要先調用calc("20",0,2)==>20,0,2,2 執行到b=1時候開始調用,index:2==>calc("2",0,2)==>2,0,2,2 最後執行index:1==>calc("1",1,3)==>1,1,3,4

10 1 2 320 0 2 22 0 2 21 1 3 4

7. 請寫出以下輸入內容

func main() {    s := make([]int, 0)    s = append(s, 1, 2, 3)    fmt.Println(s)}

考點:make預設值和append
解答: make初始化是由預設值的哦,此處預設值為0

[0 0 0 0 0 1 2 3]

大家試試改為:

s := make([]int, 0)s = append(s, 1, 2, 3)fmt.Println(s)//[1 2 3]

8. 下面的代碼有什麼問題?

type UserAges struct {ages map[string]intsync.Mutex}func (ua *UserAges) Add(name string, age int) {ua.Lock()defer ua.Unlock()ua.ages[name] = age}func (ua *UserAges) Get(name string) int {if age, ok := ua.ages[name]; ok {return age}return -1}

考點:map安全執行緒
解答:可能會出現fatal error: concurrent map read and map write. 修改一下看看效果

func (ua *UserAges) Get(name string) int {    ua.Lock()    defer ua.Unlock()    if age, ok := ua.ages[name]; ok {        return age    }    return -1}

9. 下面的迭代會有什麼問題?

func (set *threadSafeSet) Iter() <-chan interface{} {ch := make(chan interface{})go func() {set.RLock()for elem := range set.s {ch <- elem}close(ch)set.RUnlock()}()return ch}

考點:chan緩衝池
解答:看到這道題,我也在猜想出題者的意圖在哪裡。 chan?sync.RWMutex?go?chan緩衝池?迭代? 所以只能再讀一次題目,就從迭代入手看看。既然是迭代就會要求set.s全部可以遍曆一次。但是chan是為緩衝的,那就代表這寫入一次就會阻塞。我們把代碼恢複為可以啟動並執行方式,看看效果

package mainimport (    "sync"    "fmt")//下面的迭代會有什麼問題?type threadSafeSet struct {    sync.RWMutex    s []interface{}}func (set *threadSafeSet) Iter() <-chan interface{} {    // ch := make(chan interface{}) // 解除注釋看看!    ch := make(chan interface{},len(set.s))    go func() {        set.RLock()        for elem,value := range set.s {            ch <- elem            println("Iter:",elem,value)        }        close(ch)        set.RUnlock()    }()    return ch}func main()  {    th:=threadSafeSet{        s:[]interface{}{"1","2"},    }    v:=<-th.Iter()    fmt.Sprintf("%s%v","ch",v)}

10. 以下代碼能編譯過去嗎?為什嗎?

package mainimport ("fmt")type People interface {Speak(string) string}type Stduent struct{}func (stu *Stduent) Speak(think string) (talk string) {if think == "bitch" {talk = "You are a good boy"} else {talk = "hi"}return}func main() {var peo People = Stduent{}think := "bitch"fmt.Println(peo.Speak(think))}

考點:golang的方法集
解答:編譯不通過!做錯了!?說明你對golang的方法集還有一些疑問。一句話:golang的方法集僅僅影響介面實現和方法運算式轉化,與通過執行個體或者指標調用方法無關。

11. 以下代碼列印出來什麼內容,說出為什麼。

package mainimport ("fmt")type People interface {Show()}type Student struct{}func (stu *Student) Show() {}func live() People {var stu *Studentreturn stu}func main() {if live() == nil {fmt.Println("AAAAAAA")} else {fmt.Println("BBBBBBB")}}

考點:interface內部結構
解答:很經典的題!這個考點是很多人忽略的interface內部結構。 go中的介面分為兩種一種是空的介面類似這樣:

var in interface{}

另一種如題目:

type People interface {    Show()}

他們的底層結構如下:

type eface struct {      //空介面    _type *_type         //類型資訊    data  unsafe.Pointer //指向資料的指標(go語言中特殊的指標類型unsafe.Pointer類似於c語言中的void*)}type iface struct {      //帶有方法的介面    tab  *itab           //儲存type資訊還有結構實現方法的集合    data unsafe.Pointer  //指向資料的指標(go語言中特殊的指標類型unsafe.Pointer類似於c語言中的void*)}type _type struct {    size       uintptr  //類型大小    ptrdata    uintptr  //首碼持有所有指標的記憶體大小    hash       uint32   //資料hash值    tflag      tflag    align      uint8    //對齊    fieldalign uint8    //嵌入結構體時的對齊    kind       uint8    //kind 有些枚舉值kind等於0是無效的    alg        *typeAlg //函數指標數組,類型實現的所有方法    gcdata    *byte    str       nameOff    ptrToThis typeOff}type itab struct {    inter  *interfacetype  //介面類型    _type  *_type          //結構類型    link   *itab    bad    int32    inhash int32    fun    [1]uintptr      //可變大小 方法集合}

可以看出iface比eface 中間多了一層itab結構。 itab 儲存_type資訊和[]fun方法集,從上面的結構我們就可得出,因為data指向了nil 並不代表interface 是nil,所以傳回值並不為空白,這裡的fun(方法集)定義了介面的接收規則,在編譯的過程中需要驗證是否實現介面結果:

BBBBBBB


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.