1. 你能答對這道 Go 題目嗎?超過 80% 的人都答錯了...

        共 2254字,需瀏覽 5分鐘

         ·

        2022-03-18 12:45

        大家好,我是煎魚。

        最近在沖浪的時候,看到 Redhat 首席軟件工程師、Prometheus 等項目的維護(hù)者 Bart?omiej P?otka 在 twitter 上出了一道 Go 的 “考題”,說是要來考考大家。

        題目如下:

        func?aaa()?(done?func(),?err?error)?{
        ?return?func()?{?
        ???print("aaa:?done")?
        ?},?nil
        }

        func?bbb()?(done?func(),?_?error)?{
        ?done,?err?:=?aaa()
        ?return?func()?{?
        ???print("bbb:?surprise!");?
        ???done()?
        ?},?err
        }

        func?main()?{
        ?done,?_?:=?bbb()
        ?done()
        }

        答案的選項如下:

        想想原因,思考一下輸出結(jié)果是什么?是 A,還是 D?還是三短一長,選 B?

        分析程序

        縮小范圍,核心關(guān)注到這塊代碼。如下:

        func?bbb()?(done?func(),?_?error)?{
        ?done,?err?:=?aaa()
        ?return?func()?{?
        ???print("bbb:?surprise!")
        ???done()?
        ?},?err
        }

        在最后一行的這個閉包(匿名函數(shù))中,大家可能認(rèn)為程序調(diào)用了函數(shù) aaa 所返回的 done 值來輸出程序,應(yīng)當(dāng)是:

        aaa:?done

        這個想法是錯誤的,程序沒有這么去運(yùn)作。

        原因在于 return 實(shí)際上是一個賦值語句。結(jié)合程序,可以看到函數(shù) bbb 的第一個返回值是 done 參數(shù)。

        如下:

        func?bbb()?(done?func(),?_?error)

        也就是在函數(shù) bbb 在程序最后執(zhí)行 return 語句后,會對返回變量 done 進(jìn)行賦值,自然該值不會是由函數(shù) aaa 所設(shè)置的了。

        這是一個關(guān)鍵的地方。

        具體過程

        這個程序輸出結(jié)果是什么呢?

        會不斷地遞歸,瘋狂輸出 “bbb: surprise!”,直至棧溢出,導(dǎo)致程序運(yùn)行出錯,最終中止

        同學(xué)就疑惑了,怎么又多出了個遞歸?

        我們再看看程序:

        func?main()?{
        ?done,?_?:=?bbb()
        ?done()
        }

        func?bbb()?(done?func(),?_?error)?{
        ?...
        ?return?func()?{?
        ???print("bbb:?surprise!");?
        ???done()?
        ?},?err
        }

        本質(zhì)上在函數(shù) bbb 執(zhí)行完畢后,?變量 done 已經(jīng)變成了一個遞歸函數(shù)。

        遞歸的過程是:函數(shù) bbb 調(diào)用變量 done 后,會輸出 bbb: surprise! 字符串,然后又調(diào)用變量 done。而變量 done 又是這個閉包(匿名函數(shù)),從而實(shí)現(xiàn)不斷遞歸調(diào)用和輸出。

        最終結(jié)果如下:

        b:?surprise!bbb:?surprise!bbb:?surprise!runtime:?goroutine?stack?exceeds?1000000000-byte?limit
        runtime:?sp=0xc0200e0380?stack=[0xc0200e0000,?0xc0400e0000]
        fatal?error:?stack?overflow

        runtime?stack:
        runtime.throw(0x1074b5a,?0xe)
        ????????/usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:1117?+0x72
        runtime.newstack()
        ????????/usr/local/Cellar/go/1.16.6/libexec/src/runtime/stack.go:1069?+0x7ed
        runtime.morestack()
        ????????/usr/local/Cellar/go/1.16.6/libexec/src/runtime/asm_amd64.s:458?+0x8f
        ...

        也就是正確答案是:D,程序最終運(yùn)行出錯。

        一直調(diào)用一直爽,直至棧溢出程序崩潰。

        總結(jié)

        這位大佬出的題目,本質(zhì)上是比較煩人的,其結(jié)合了函數(shù)返回參數(shù)的命名用法。

        如果我們把這個函數(shù)的返回參數(shù)命名去掉,就可以避開這個問題。如下:

        func?bbb()?(func(),?error)?{
        ?...
        ?return?func()?{?
        ???print("bbb:?surprise!");?
        ???done()?
        ?},?err
        }
        ...

        輸出結(jié)果為 "bbb: surprise!"。

        很多 Go 的同學(xué)在日常代碼編寫的時候不會用到或注意到。但如果寫的時候有類似案例代碼中的模式,就會排查許久都查不到。

        這是個有警惕意義的題目,你覺得呢?

        參考

        • twitter 上的原題目
        • Go 語言返回值是引用時return后省略是推薦寫法嗎?

        關(guān)注煎魚,獲取業(yè)內(nèi)第一手消息和知識 ??



        你好,我是煎魚,出版過 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領(lǐng)域最有觀點(diǎn)專家)榮譽(yù),點(diǎn)擊藍(lán)字查看我的出書之路。

        日常分享高質(zhì)量文章,輸出 Go 面試、工作經(jīng)驗、架構(gòu)設(shè)計,加微信拉讀者交流群,和大家交流!

        瀏覽 35
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 裸体男男直播1o69ga丫 | 肏逼的小说 | 男人的天堂aaa | 激情性爱av| 香港三级毛片 |