日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

经典Golang语法50问!

發布時間:2025/6/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 经典Golang语法50问! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

Golang 這門語言想必大家都不陌生,現在也比較火,學習的人也比較多。作為一款性能還算不錯的語言,現在很多大廠的新項目都選擇了Golang。

這期針對大家的疑惑,總結了大廠系列Golang語法50問,供大家面試和學習用,下面看一下提綱。

1.使用值為?nil?的?slice、map會發生啥

允許對值為 nil 的 slice 添加元素,但對值為 nil 的 map 添加元素,則會造成運行時 panic。

//?map?錯誤示例 func?main()?{var?m?map[string]intm["one"]?=?1??//?error:?panic:?assignment?to?entry?in?nil?map//?m?:=?make(map[string]int)//?map?的正確聲明,分配了實際的內存 }????//?slice?正確示例 func?main()?{var?s?[]ints?=?append(s,?1) }

?

2.訪問 map 中的 key,需要注意啥

當訪問 map 中不存在的 key 時,Go 則會返回元素對應數據類型的零值,比如 nil、''?、false 和?0,取值操作總有值返回,故不能通過取出來的值,來判斷 key 是不是在 map 中。

檢查 key 是否存在可以用 map 直接訪問,檢查返回的第二個參數即可。

//?錯誤的?key?檢測方式 func?main()?{x?:=?map[string]string{"one":?"2",?"two":?"",?"three":?"3"}if?v?:=?x["two"];?v?==?""?{fmt.Println("key?two?is?no?entry")?//?鍵?two?存不存在都會返回的空字符串} }//?正確示例 func?main()?{x?:=?map[string]string{"one":?"2",?"two":?"",?"three":?"3"}if?_,?ok?:=?x["two"];?!ok?{fmt.Println("key?two?is?no?entry")} }

?

3.string 類型的值可以修改嗎

不能,嘗試使用索引遍歷字符串,來更新字符串中的個別字符,是不允許的。
?

string 類型的值是只讀的二進制 byte slice,如果真要修改字符串中的字符,將 string 轉為?[]byte 修改后,再轉為 string 即可。

//?修改字符串的錯誤示例 func?main()?{x?:=?"text"x[0]?=?"T"??//?error:?cannot?assign?to?x[0]fmt.Println(x) }//?修改示例 func?main()?{x?:=?"text"xBytes?:=?[]byte(x)xBytes[0]?=?'T'?//?注意此時的?T?是?rune?類型x?=?string(xBytes)fmt.Println(x)?//?Text }

?

4.switch 中如何強制執行下一個 case 代碼塊

switch 語句中的 case 代碼塊會默認帶上 break,但可以使用?fallthrough 來強制執行下一個 case 代碼塊。

func?main()?{isSpace?:=?func(char?byte)?bool?{switch?char?{case?'?':?//?空格符會直接?break,返回?false?//?和其他語言不一樣//?fallthrough?//?返回?truecase?'\t':return?true}return?false}fmt.Println(isSpace('\t'))?//?truefmt.Println(isSpace('?'))?//?false }

?

5.你是如何關閉 HTTP 的響應體的

直接在處理 HTTP 響應錯誤的代碼塊中,直接關閉非 nil 的響應體;手動調用 defer 來關閉響應體。

//?正確示例 func?main()?{resp,?err?:=?http.Get("http://www.baidu.com")//?關閉?resp.Body?的正確姿勢if?resp?!=?nil?{defer?resp.Body.Close()}checkError(err)defer?resp.Body.Close()body,?err?:=?ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body)) }

?

6.你是否主動關閉過http連接,為啥要這樣做

有關閉,不關閉會程序可能會消耗完 socket 描述符。有如下2種關閉方式:

  • 直接設置請求變量的?Close 字段值為 true,每次請求結束后就會主動關閉連接。

    設置?Header?請求頭部選項?Connection:?close,然后服務器返回的響應頭部也會有這個選項,此時?HTTP?標準庫會主動斷開連接

    //?主動關閉連接 func?main()?{req,?err?:=?http.NewRequest("GET",?"http://golang.org",?nil)checkError(err)req.Close?=?true//req.Header.Add("Connection",?"close")?//?等效的關閉方式resp,?err?:=?http.DefaultClient.Do(req)if?resp?!=?nil?{defer?resp.Body.Close()}checkError(err)body,?err?:=?ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(body)) }

你可以創建一個自定義配置的?HTTP transport?客戶端,用來取消 HTTP 全局的復用連接。

func?main()?{tr?:=?http.Transport{DisableKeepAlives:?true}client?:=?http.Client{Transport:?&tr}resp,?err?:=?client.Get("https://golang.google.cn/")if?resp?!=?nil?{defer?resp.Body.Close()}checkError(err)fmt.Println(resp.StatusCode)?//?200body,?err?:=?ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(len(string(body))) }

?

7.解析 JSON 數據時,默認將數值當做哪種類型

在 encode/decode JSON 數據時,Go 默認會將數值當做?float64?處理。

func?main()?{var?data?=?[]byte(`{"status":?200}`)var?result?map[string]interface{}if?err?:=?json.Unmarshal(data,?&result);?err?!=?nil?{log.Fatalln(err) }

解析出來的 200 是 float 類型。
?

8.如何從 panic 中恢復

在一個 defer 延遲執行的函數中調用 recover?,它便能捕捉/中斷 panic。

//?錯誤的?recover?調用示例 func?main()?{recover()?//?什么都不會捕捉panic("not?good")?//?發生?panic,主程序退出recover()?//?不會被執行println("ok") }//?正確的?recover?調用示例 func?main()?{defer?func()?{fmt.Println("recovered:?",?recover())}()panic("not?good") }

?

9.簡短聲明的變量需要注意啥

  • 簡短聲明的變量只能在函數內部使用

  • struct?的變量字段不能使用?:=?來賦值

  • 不能用簡短聲明方式來單獨為一個變量重復聲明,?:=?左側至少有一個新變量,才允許多變量的重復聲明
    ?

10.range?迭代 map是有序的嗎

無序的。Go 的運行時是有意打亂迭代順序的,所以你得到的迭代結果可能不一致。但也并不總會打亂,得到連續相同的 5 個迭代結果也是可能的。
?

11.recover的執行時機

無,recover 必須在?defer 函數中運行。recover 捕獲的是祖父級調用時的異常,直接調用時無效。

func?main()?{recover()panic(1) }

?

直接 defer 調用也是無效。

func?main()?{defer?recover()panic(1) }

?

defer 調用時多層嵌套依然無效。

func?main()?{defer?func()?{func()?{?recover()?}()}()panic(1) }

?

必須在 defer 函數中直接調用才有效。

func?main()?{defer?func()?{recover()}()panic(1) }

?

12.閉包錯誤引用同一個變量問題怎么處理

在每輪迭代中生成一個局部變量 i 。如果沒有 i := i 這行,將會打印同一個變量。?

func?main()?{for?i?:=?0;?i?<?5;?i++?{i?:=?idefer?func()?{println(i)}()} }


或者是通過函數參數傳入 i 。

func?main()?{for?i?:=?0;?i?<?5;?i++?{defer?func(i?int)?{println(i)}(i)} }

?

13.在循環內部執行defer語句會發生啥

defer 在函數退出時才能執行,在 for 執行 defer 會導致資源延遲釋放。

func?main()?{for?i?:=?0;?i?<?5;?i++?{func()?{f,?err?:=?os.Open("/path/to/file")if?err?!=?nil?{log.Fatal(err)}defer?f.Close()}()} }

func 是一個局部函數,在局部函數里面執行 defer 將不會有問題。

14.說出一個避免Goroutine泄露的措施

可以通過?context 包來避免內存泄漏。

func?main()?{ctx,?cancel?:=?context.WithCancel(context.Background())ch?:=?func(ctx?context.Context)?<-chan?int?{ch?:=?make(chan?int)go?func()?{for?i?:=?0;?;?i++?{select?{case?<-?ctx.Done():returncase?ch?<-?i:}}}?()return?ch}(ctx)for?v?:=?range?ch?{fmt.Println(v)if?v?==?5?{cancel()break}} }

下面的 for 循環停止取數據時,就用 cancel 函數,讓另一個協程停止寫數據。如果下面 for 已停止讀取數據,上面 for 循環還在寫入,就會造成內存泄漏。

?

15.如何跳出for select 循環

通常在for循環中,使用break可以跳出循環,但是注意在go語言中,for select配合時,break 并不能跳出循環。

func?testSelectFor2(chExit?chan?bool){EXIT:for??{select?{case?v,?ok?:=?<-chExit:if?!ok?{fmt.Println("close?channel?2",?v)break?EXIT//goto?EXIT2}fmt.Println("ch2?val?=",?v)}}//EXIT2:fmt.Println("exit?testSelectFor2") }

?

16.如何在切片中查找

go中使用 sort.searchXXX 方法,在排序好的切片中查找指定的方法,但是其返回是對應的查找元素不存在時,待插入的位置下標(元素插入在返回下標前)。

可以通過封裝如下函數,達到目的。

func?IsExist(s?[]string,?t?string)?(int,?bool)?{iIndex?:=?sort.SearchStrings(s,?t)bExist?:=?iIndex!=len(s)?&&?s[iIndex]==treturn?iIndex,?bExist }

?

17.如何初始化帶嵌套結構的結構體

go 的哲學是組合優于繼承,使用 struct 嵌套即可完成組合,內嵌的結構體屬性就像外層結構的屬性即可,可以直接調用。

注意初始化外層結構體時,必須指定內嵌結構體名稱的結構體初始化,如下看到 s1方式報錯,s2 方式正確。

type?stPeople?struct?{Gender?boolName?string }type?stStudent?struct?{stPeopleClass?int }//嘗試4?嵌套結構的初始化表達式 //var?s1?=?stStudent{false,?"JimWen",?3} var?s2?=?stStudent{stPeople{false,?"JimWen"},?3} fmt.Println(s2.Gender,?s2.Name,?s2.Class)

?

18.切片和數組的區別

數組是具有固定長度,且擁有零個或者多個,相同數據類型元素的序列。數組的長度是數組類型的一部分,所以[3]int 和 [4]int 是兩種不同的數組類型。

數組需要指定大小,不指定也會根據初始化的自動推算出大小,不可改變;數組是值傳遞。

數組是內置類型,是一組同類型數據的集合,它是值類型,通過從0開始的下標索引訪問元素值。在初始化后長度是固定的,無法修改其長度。

當作為方法的參數傳入時將復制一份數組而不是引用同一指針。數組的長度也是其類型的一部分,通過內置函數len(array)獲取其長度。

數組定義:

?
  • var array [10]int

  • ?

  • var array =[5]int{1,2,3,4,5}


  • 切片表示一個擁有相同類型元素的可變長度的序列。切片是一種輕量級的數據結構,它有三個屬性:指針、長度和容量。

    切片不需要指定大小;切片是地址傳遞;切片可以通過數組來初始化,也可以通過內置函數make()初始化 。初始化時len=cap,在追加元素時如果容量cap不足時將按len的2倍擴容。

    切片定義:

    var slice []type = make([]type, len)

    ?

    19.new和make的區別

    new 的作用是初始化一個指向類型的指針?(*T) 。

    new 函數是內建函數,函數定義:func new(Type) *Type。

    使用 new 函數來分配空間。傳遞給 new 函數的是一個類型,不是一個值。返回值是指向這個新分配的零值的指針。
    ?

    make 的作用是為 slice,map 或 chan 初始化并返回引用 (T)。

    make 函數是內建函數,函數定義:func make(Type, size IntegerType) Type;第一個參數是一個類型,第二個參數是長度;返回值是一個類型。
    ?

    make(T, args) 函數的目的與 new(T) 不同。它僅僅用于創建 Slice, Map 和 Channel,并且返回類型是 T(不是T*)的一個初始化的(不是零值)的實例。

    20.Printf()、Sprintf()、Fprintf()函數的區別用法是什么

    都是把格式好的字符串輸出,只是輸出的目標不一樣。
    ?

    Printf(),是把格式字符串輸出到標準輸出(一般是屏幕,可以重定向)。Printf() 是和標準輸出文件 (stdout) 關聯的,Fprintf ?則沒有這個限制。

    ?

    Sprintf(),是把格式字符串輸出到指定字符串中,所以參數比printf多一個char*。那就是目標字符串地址。
    ?

    Fprintf(),是把格式字符串輸出到指定文件設備中,所以參數比 printf 多一個文件指針 FILE*。主要用于文件操作。Fprintf() 是格式化輸出到一個stream,通常是到文件。

    21.說說go語言中的for循環

    for 循環支持 continue 和 break 來控制循環,但是它提供了一個更高級的break,可以選擇中斷哪一個循環 for 循環不支持以逗號為間隔的多個賦值語句,必須使用平行賦值的方式來初始化多個變量。
    ?

    22.Array?類型的值作為函數參數

    在 C/C++?中,數組(名)是指針。將數組作為參數傳進函數時,相當于傳遞了數組內存地址的引用,在函數內部會改變該數組的值。

    在 Go 中,數組是值。作為參數傳進函數時,傳遞的是數組的原始值拷貝,此時在函數內部是無法更新該數組的。

    //?數組使用值拷貝傳參 func?main()?{x?:=?[3]int{1,2,3}func(arr?[3]int)?{arr[0]?=?7fmt.Println(arr)?//?[7?2?3]}(x)fmt.Println(x)???//?[1?2?3]?//?并不是你以為的?[7?2?3] }


    想改變數組,直接傳遞指向這個數組的指針類型。

    //?傳址會修改原數據 func?main()?{x?:=?[3]int{1,2,3}func(arr?*[3]int)?{(*arr)[0]?=?7?fmt.Println(arr)?//?&[7?2?3]}(&x)fmt.Println(x)?//?[7?2?3] }


    直接使用 slice:即使函數內部得到的是 slice 的值拷貝,但依舊會更新 slice 的原始數據(底層 array)

    //?錯誤示例 func?main()?{x?:=?[]string{"a",?"b",?"c"}for?v?:=?range?x?{fmt.Println(v)?//?1?2?3} }//?正確示例 func?main()?{x?:=?[]string{"a",?"b",?"c"}for?_,?v?:=?range?x?{?//?使用?_?丟棄索引fmt.Println(v)} }

    說。go語言中的for循

    23.說說go語言中的switch語句

    單個 case 中,可以出現多個結果選項。只有在 case 中明確添加?fallthrough關鍵字,才會繼續執行緊跟的下一個 case。
    ?

    24.說說go語言中有沒有隱藏的this指針

    方法施加的對象顯式傳遞,沒有被隱藏起來。
    ?

    golang 的面向對象表達更直觀,對于面向過程只是換了一種語法形式來表達

    方法施加的對象不需要非得是指針,也不用非得叫 this。
    ?

    25.go語言中的引用類型包含哪些

    數組切片、字典(map)、通道(channel)、接口(interface)。
    ?

    26.go語言中指針運算有哪些

    可以通過“&”取指針的地址;可以通過“*”取指針指向的數據。
    ?

    26.說說go語言的main函數

    main 函數不能帶參數;main 函數不能定義返回值。

    main 函數所在的包必須為?main 包;main 函數中可以使用 flag 包來獲取和解析命令行參數。
    ?

    27.go語言觸發異常的場景有哪些

    • 空指針解析

    • 下標越界

    • 除數為0

    • 調用 panic 函數
      ?

    28.說說go語言的beego框架

    • beego 是一個 golang 實現的輕量級HTTP框架

    • beego 可以通過注釋路由、正則路由等多種方式完成 url 路由注入

    • 可以使用 bee new 工具生成空工程,然后使用 bee run 命令自動熱編譯

    ?

    29.說說go語言的goconvey框架

    • goconvey 是一個支持 golang 的單元測試框架

    • goconvey 能夠自動監控文件修改并啟動測試,并可以將測試結果實時輸出到web界面

    • goconvey 提供了豐富的斷言簡化測試用例的編寫
      ?

    30.GoStub的作用是什么

    • GoStub 可以對全局變量打樁

    • GoStub 可以對函數打樁

    • GoStub 不可以對類的成員方法打樁

    • GoStub 可以打動態樁,比如對一個函數打樁后,多次調用該函數會有不同的行為
      ?

    31.go語言編程的好處是什么

    • 編譯和運行都很快。

    • 在語言層級支持并行操作。

    • 有垃圾處理器。

    • 內置字符串和 maps。

    • 函數是 go 語言的最基本編程單位。
      ?

    32.說說go語言的select機制

    • select 機制用來處理異步 IO?問題

    • select 機制最大的一條限制就是每個 case 語句里必須是一個 IO 操作

    • golang 在語言級別支持 select 關鍵字

    ?

    33.解釋一下go語言中的靜態類型聲明

    靜態類型聲明是告訴編譯器不需要太多的關注這個變量的細節。

    靜態變量的聲明,只是針對于編譯的時候, 在連接程序的時候,編譯器還要對這個變量進行實際的聲明。

    ?

    34.go的接口是什么

    在 go 語言中,interface 也就是接口,被用來指定一個對象。接口具有下面的要素:

    • 一系列的方法

    • 具體應用中并用來表示某個數據類型

    • 在 go 中使用 interface 來實現多態
      ?

    35.Go語言里面的類型斷言是怎么回事

    類型斷言是用來從一個接口里面讀取數值給一個具體的類型變量。

    類型轉換是指轉換兩個不相同的數據類型。
    ?

    36.go語言中局部變量和全局變量的缺省值是什么

    全局變量的缺省值是與這個類型相關的零值。

    ?

    37.go語言編程的好處是什么

    • 編譯和運行都很快。

    • 在語言層級支持并行操作。

    • 有垃圾處理器。

    • 內置字符串和 maps。

    • 函數是 go 語言的最基本編程單位。
      ?

    38.解釋一下go語言中的靜態類型聲明

    靜態類型聲明是告訴編譯器不需要太多的關注這個變量的細節。
    靜態變量的聲明,只是針對于編譯的時候, 在連接程序的時候,編譯器還要對這個變量進行實際的聲明。

    ?

    39.模塊化編程是怎么回事

    模塊化編程是指把一個大的程序分解成幾個小的程序。這么做的目的是為了減少程序的復雜度,易于維護,并且達到最高的效率。
    ?

    碼字不易,請不吝點贊,隨手關注,更多精彩,自動送達。
    ?

    40.Golang的方法有什么特別之處

    函數的定義聲明沒有接收者。

    ?

    方法的聲明和函數類似,他們的區別是:方法在定義的時候,會在func和方法名之間增加一個參數,這個參數就是接收者,這樣我們定義的這個方法就和接收者綁定在了一起,稱之為這個接收者的方法。

    ?

    Go語言里有兩種類型的接收者:值接收者和指針接收者。

    使用值類型接收者定義的方法,在調用的時候,使用的其實是值接收者的一個副本,所以對該值的任何操作,不會影響原來的類型變量。-------相當于形式參數。
    ?

    如果我們使用一個指針作為接收者,那么就會其作用了,因為指針接收者傳遞的是一個指向原值指針的副本,指針的副本,指向的還是原來類型的值,所以修改時,同時也會影響原來類型變量的值。

    ?

    41.Golang可變參數

    函數方法的參數,可以是任意多個,這種我們稱之為可以變參數,比如我們常用的fmt.Println()這類函數,可以接收一個可變的參數。

    可以變參數,可以是任意多個。我們自己也可以定義可以變參數,可變參數的定義,在類型前加上省略號…即可。

    func?main()?{print("1","2","3") }func?print?(a?...interface{}){for?_,v:=range?a{fmt.Print(v)}fmt.Println() }

    例子中我們自己定義了一個接受可變參數的函數,效果和fmt.Println()一樣。

    可變參數本質上是一個數組,所以我們向使用數組一樣使用它,比如例子中的 for range 循環。

    42.Golang Slice的底層實現

    切片是基于數組實現的,它的底層是數組,它自己本身非常小,可以理解為對底層數組的抽象。因為基于數組實現,所以它的底層的內存是連續分配的,效率非常高,還可以通過索引獲得數據,可以迭代以及垃圾回收優化。

    ?

    切片本身并不是動態數組或者數組指針。它內部實現的數據結構通過指針引用底層數組,設定相關屬性將數據讀寫操作限定在指定的區域內。切片本身是一個只讀對象,其工作機制類似數組指針的一種封裝。

    ?

    切片對象非常小,是因為它是只有3個字段的數據結構:

    • 指向底層數組的指針

    • 切片的長度

    • 切片的容量
      ?

    這3個字段,就是Go語言操作底層數組的元數據。


    ?

    43.Golang Slice的擴容機制,有什么注意點

    Go 中切片擴容的策略是這樣的:

    首先判斷,如果新申請容量大于 2?倍的舊容量,最終容量就是新申請的容量。

    否則判斷,如果舊切片的長度小于 1024,則最終容量就是舊容量的兩倍。

    否則判斷,如果舊切片長度大于等于 1024,則最終容量從舊容量開始循環增加原來的? 1/4 , 直到最終容量大于等于新申請的容量。

    如果最終容量計算值溢出,則最終容量就是新申請容量。

    ?

    情況一:

    原數組還有容量可以擴容(實際容量沒有填充完),這種情況下,擴容以后的數組還是指向原來的數組,對一個切片的操作可能影響多個指針指向相同地址的Slice。

    ?

    情況二:

    原來數組的容量已經達到了最大值,再想擴容, Go 默認會先開一片內存區域,把原來的值拷貝過來,然后再執行 append() 操作。這種情況絲毫不影響原數組。

    ?

    要復制一個Slice,最好使用Copy函數。
    ?

    44.Golang Map底層實現

    Golang 中 map 的底層實現是一個散列表,因此實現 map 的過程實際上就是實現散表的過程。
    在這個散列表中,主要出現的結構體有兩個,一個叫hmap(a header for a go map),一個叫bmap(a bucket for a Go map,通常叫其bucket)。

    hmap如下所示:
    ?

    ?

    圖中有很多字段,但是便于理解 map 的架構,你只需要關心的只有一個,就是標紅的字段:buckets 數組。Golang 的 map 中用于存儲的結構是 bucket數組。而 bucket(即bmap)的結構是怎樣的呢?

    ?

    bucket:
    ?

    ?

    相比于 hmap,bucket 的結構顯得簡單一些,標橙的字段依然是“核心”,我們使用的 map 中的 key 和 value 就存儲在這里。

    "高位哈希值"數組記錄的是當前 bucket 中 key 相關的"索引",稍后會詳細敘述。還有一個字段是一個指向擴容后的 bucket 的指針,使得 bucket 會形成一個鏈表結構。

    ?

    整體的結構應該是這樣的:

    ?

    Golang 把求得的哈希值按照用途一分為二:高位和低位。低位用于尋找當前 key屬于 hmap 中的哪個 bucket,而高位用于尋找 bucket 中的哪個 key。

    ?

    需要特別指出的一點是:map中的key/value值都是存到同一個數組中的。這樣做的好處是:在key和value的長度不同的時候,可以消除padding帶來的空間浪費。

    ?

    ?

    Map 的擴容:

    當 Go 的 map 長度增長到大于加載因子所需的 map 長度時,Go 語言就會將產生一個新的 bucket 數組,然后把舊的 bucket 數組移到一個屬性字段 oldbucket中。
    ?

    注意:并不是立刻把舊的數組中的元素轉義到新的 bucket 當中,而是,只有當訪問到具體的某個 bucket 的時候,會把 bucket 中的數據轉移到新的 bucket 中。

    ?

    45.?JSON 標準庫對 nil slice 和 空 slice 的處理是一致的嗎

    首先 JSON 標準庫對 nil slice 和 空 slice 的處理是不一致。
    ?

    通常錯誤的用法,會報數組越界的錯誤,因為只是聲明了slice,卻沒有給實例化的對象。

    var?slice?[]int slice[1]?=?0

    此時slice的值是nil,這種情況可以用于需要返回slice的函數,當函數出現異常的時候,保證函數依然會有nil的返回值。
    ?

    empty slice 是指slice不為nil,但是slice沒有值,slice的底層的空間是空的,此時的定義如下:

    slice?:=?make([]int,0) slice?:=?[]int{}

    當我們查詢或者處理一個空的列表的時候,這非常有用,它會告訴我們返回的是一個列表,但是列表內沒有任何值。

    總之,nil slice 和 empty slice是不同的東西,需要我們加以區分的。

    ?

    46.Golang的內存模型,為什么小對象多了會造成gc壓力

    通常小對象過多會導致 GC 三色法消耗過多的GPU。優化思路是,減少對象分配。

    ?

    47.Data Race問題怎么解決?能不能不加鎖解決這個問題

    同步訪問共享數據是處理數據競爭的一種有效的方法。

    golang在 1.1 之后引入了競爭檢測機制,可以使用 go run -race 或者 go build -race來進行靜態檢測。其在內部的實現是,開啟多個協程執行同一個命令, 并且記錄下每個變量的狀態。
    ?

    競爭檢測器基于C/C++的ThreadSanitizer 運行時庫,該庫在Google內部代碼基地和Chromium找到許多錯誤。這個技術在2012年九月集成到Go中,從那時開始,它已經在標準庫中檢測到42個競爭條件?,F在,它已經是我們持續構建過程的一部分,當競爭條件出現時,它會繼續捕捉到這些錯誤。
    ?

    競爭檢測器已經完全集成到Go工具鏈中,僅僅添加-race標志到命令行就使用了檢測器。

    $?go?test?-race?mypkg????//?測試包 $?go?run?-race?mysrc.go??//?編譯和運行程序?$?go?build?-race?mycmd? //?構建程序?$?go?install?-race?mypkg?//?安裝程序


    要想解決數據競爭的問題可以使用互斥鎖sync.Mutex,解決數據競爭(Data race),也可以使用管道解決,使用管道的效率要比互斥鎖高。

    ?

    48.在?range?迭代?slice?時,你怎么修改值的

    在 range 迭代中,得到的值其實是元素的一份值拷貝,更新拷貝并不會更改原來的元素,即是拷貝的地址并不是原有元素的地址。

    func?main()?{data?:=?[]int{1,?2,?3}for?_,?v?:=?range?data?{v?*=?10??//?data?中原有元素是不會被修改的}fmt.Println("data:?",?data)?//?data:??[1?2?3] }


    如果要修改原有元素的值,應該使用索引直接訪問。

    func?main()?{data?:=?[]int{1,?2,?3}for?i,?v?:=?range?data?{data[i]?=?v?*?10?}fmt.Println("data:?",?data)?//?data:??[10?20?30] }


    如果你的集合保存的是指向值的指針,需稍作修改。依舊需要使用索引訪問元素,不過可以使用 range 出來的元素直接更新原有值。

    func?main()?{data?:=?[]*struct{?num?int?}{{1},?{2},?{3},}for?_,?v?:=?range?data?{v.num?*=?10?//?直接使用指針更新}fmt.Println(data[0],?data[1],?data[2])?//?&{10}?&{20}?&{30} }

    ?

    49.nil interface 和 nil interface?的區別

    雖然 interface 看起來像指針類型,但它不是。interface 類型的變量只有在類型和值均為 nil 時才為 nil

    如果你的 interface 變量的值是跟隨其他變量變化的,與 nil 比較相等時小心。

    如果你的函數返回值類型是 interface,更要小心這個坑:

    func?main()?{var?data?*bytevar?in?interface{}fmt.Println(data,?data?==?nil)?//?<nil>?truefmt.Println(in,?in?==?nil)?//?<nil>?truein?=?datafmt.Println(in,?in?==?nil)?//?<nil>?false?//?data?值為?nil,但?in?值不為?nil }//?正確示例 func?main()?{doIt?:=?func(arg?int)?interface{}?{var?result?*struct{}?=?nilif?arg?>?0?{result?=?&struct{}{}}?else?{return?nil?//?明確指明返回?nil}return?result}if?res?:=?doIt(-1);?res?!=?nil?{fmt.Println("Good?result:?",?res)}?else?{fmt.Println("Bad?result:?",?res)?//?Bad?result:?<nil>} }

    ?

    50.select可以用于什么

    常用語gorotine的完美退出。
    ?

    golang 的 select 就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作

    每個case語句里必須是一個IO操作,確切的說,應該是一個面向channel的IO操作。

    ?

    結尾

    本文總結了常見的 Go 語法問題,且在工作和面試中都容易出錯的。可以供大家學習和面試用。

    Golang 的開源社區在不斷的壯大,是一門值得學習的語言。我公眾號有Golang 學習的全面資料,可以領取,祝大家學習愉快!

    ?


    微信搜索【盼盼編程】關注這個不一樣的程序員。作者介紹:CSDN top40,原創文章1000+, 資深/Golang/C++開發,現就職于知名互聯網大廠。本號分享后臺開發技術,包括算法與數據結構,筆試/面試,網絡編程,數據庫理論,操作系統,IT雜文

    總結

    以上是生活随笔為你收集整理的经典Golang语法50问!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。