<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試題解析:這么一道“簡單”的 Go 題,為什么結(jié)果出乎意料

          2020-09-29 00:48


          昨天在《Go語言愛好者周刊:第62期》中貼了一道 Go101 的題,原題如下:

          package?main

          const?s?=?"Go101.org"
          //?len(s)?==?9
          //?1?<
          //?512?/?128?==?4

          var?a?byte?=?1?<len
          (s)?/?128
          var?b?byte?=?1?<len(s[:])?/?128

          func?main()?{
          ??println(a,?b)
          }

          答案是 4 0。

          不少人對這個結(jié)果應該很吃驚,因為從答題結(jié)果看,不到一半的人答對了。而且,如果只給 var b byte = 1 << len(s[:]) / 128,沒有 a 對比,我想答對的人會更少。因為有對比,很多人雖然直覺是 4 4,但想到一定有陷阱,所以會重新思考。

          好幾個群都問,為什么結(jié)果會是 4 0,希望我解釋下。因此有了此文。

          這個小題涉及到幾個知識點。

          len 函數(shù)的結(jié)果

          要注意,len 是一個內(nèi)置函數(shù)。在官方標準庫文檔關(guān)于 len 函數(shù)[1]有這么一句:

          For some arguments, such as a string literal or a simple array expression, the result can be a constant. See the Go language specification's "Length and capacity" section for details.

          明確支持,當參數(shù)是字符串字面量和簡單 array 表達式,len 函數(shù)返回值是常量,這很重要。

          上題中,如果 const s = "Go101.org” 改為 var s = "Go101.org" 結(jié)果又會是什么呢?

          package?main

          var?s?=?"Go101.org"

          var?a?byte?=?1?<len(s)?/?128
          var?b?byte?=?1?<len(s[:])?/?128

          func?main()?{
          ?println(a,?b)
          }

          結(jié)果是 0 0。

          但改為這樣:

          package?main

          var?s?=?[9]byte{'G',?'o',?'1',?'0',?'1',?'.',?'o',?'r',?'g'}

          var?a?byte?=?1?<len(s)?/?128
          var?b?byte?=?1?<len(s[:])?/?128

          func?main()?{
          ?println(a,?b)
          }

          結(jié)果又是 4 0。

          接著看文檔那句話的后半句,查看 Go 語言規(guī)范中關(guān)于長度和容量的說明[2]。

          內(nèi)置函數(shù) len 和 cap 獲取各種類型的實參并返回一個 int 類型結(jié)果。實現(xiàn)會保證結(jié)果總是一個 int 值。

          如果 s 是一個字符串常量,那么 len(s) 是一個常量 。如果 s 類型是一個數(shù)組或到數(shù)組的指針且表達式 s 不包含 信道接收 或(非常量的) 函數(shù)調(diào)用的話, 那么表達式 len(s) 和 cap(s) 是常量;這種情況下, s 是不求值的。否則的話, len 和 cap 的調(diào)用結(jié)果不是常量且 s 會被求值。

          可見題目中:

          var?a?byte?=?1?<len(s)?/?128
          var?b?byte?=?1?<len(s[:])?/?128

          第一句的 len(s) 是常量(因為 s 是字符串常量);而第二句的 len(s[:]) 不是常量。這是這兩條語句的唯一區(qū)別:兩個 len 的返回結(jié)果數(shù)值并無差異,都是 9,但一個是常量一個不是。

          關(guān)于位移操作

          根據(jù)上面的分析,現(xiàn)在問題的關(guān)鍵在于位移運算這里。Go 語言規(guī)范中有這么一句[3]

          The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.

          大意是:在位移表達式的右側(cè)的操作數(shù)必須為整數(shù)類型,或者可以被 uint 類型的值所表示的無類型的常量。如果一個非常量位移表達式的左側(cè)的操作數(shù)是一個無類型常量,那么它會先被隱式地轉(zhuǎn)換為假如位移表達式被其左側(cè)操作數(shù)單獨替換后的類型。

          這里的關(guān)鍵在于常量位移表達式。根據(jù)上文的分析,1 << len(s) 是常量位移表達式,而 1 << len(s[:]) 不是。

          規(guī)范上關(guān)于常量表達式中,還有這么一句[4]

          If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.

          大意是:如果常量 位移表達式 的左側(cè)操作數(shù)是一個無類型常量,那么其結(jié)果是一個整數(shù)常量;否則就是和左側(cè)操作數(shù)同一類型的常量(必須是 整數(shù)類型 )

          因此對于 var a byte = 1 << len(s) / 128,因為 1 << len(s) 是一個常量位移表達式,因此它的結(jié)果也是一個整數(shù)常量,所以是 512,最后除以 128,最終結(jié)果就是 4。

          而對于 var b byte = 1 << len(s[:]) / 128,因為 1 << len(s[:]) 不是一個常量位移表達式,而做操作數(shù)是 1,一個無類型常量,根據(jù)規(guī)范定義它是 byte 類型(根據(jù):如果一個非常量位移表達式的左側(cè)的操作數(shù)是一個無類號常量,那么它會先被隱式地轉(zhuǎn)換為假如位移表達式被其左側(cè)操作數(shù)單獨替換后的類型)。

          為什么是 byte 類型,大家可能還是有點暈。這要回到關(guān)于常量的說明上。

          常量

          常量是在編譯的時候進行計算的。在 Go 語言中,常量分兩種:無類型和有類型。Go 規(guī)范上說,字面值常量, true , false , iota 以及一些僅包含無類型的恒定操作數(shù)的 常量表達式 是無類型的。

          那有類型常量是怎么來的呢?一般有兩種:顯示聲明或隱式得到。比如:

          const?a?int32?=?23
          const?b?float32?=?0.1

          無類型常量都有一個默認類型(無類型常量的默認類型分別是 bool , rune , int , float64 , complex128 或 string)。當在上下文中需要請求該常量為一個帶類型的值時,這個 默認類型 便指向該常量隱式轉(zhuǎn)換后的類型。

          所以 var b byte = 1 << len(s[:]) / 128 中,根據(jù)規(guī)范定義,1 會隱式轉(zhuǎn)換為 byte 類型,因此 1 << len(s[:]) 的結(jié)果也是 byte 類型,而 byte 類型最大只能表示 255,很顯然 512 溢出了,結(jié)果為 0,因此最后 b 的結(jié)果也是 0。

          小結(jié)

          一道很具迷惑性的題目引出這么多小知識點??赡苡腥艘獓姡河懻撨@些有什么用?這也太細節(jié)了。我想說的是,Go 語言規(guī)范,細節(jié)點很多,能多掌握一些沒壞處,說不定將來實際工作就遇到了類似的問題呢?!以上的知識點,很細節(jié),但我認為也是挺有價值的。

          當然了,你怎么說都行,你都是對的,你開心就好!

          參考資料

          [1]

          關(guān)于 len 函數(shù): https://docs.studygolang.com/pkg/builtin/#len

          [2]

          關(guān)于長度和容量的說明: https://hao.studygolang.com/golang_spec.html#id221

          [3]

          這么一句: https://docs.studygolang.com/ref/spec#Operators

          [4]

          這么一句: https://docs.studygolang.com/ref/spec#Constant_expressions



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包(下圖只是部分),同時還包含學習建議:入門看什么,進階看什么。

          關(guān)注公眾號 「polarisxu」,回復?ebook?獲?。贿€可以回復「進群」,和數(shù)萬 Gopher 交流學習。



          瀏覽 21
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  理论亚洲成人电影 | 俺来也图片区视频区 | 久色婷婷在线 | 麻豆国产一区 | 国产鸡吧三级视频黄色 |