Q1 = 和 := 的區(qū)別?
:= 聲明+賦值
= 僅賦值
var foo int
foo = 10
// 等價于
foo := 10
Q2 指針的作用?
指針用來保存變量的地址。
例如
var x = 5
var p *int = &x
fmt.Printf("x = %d", *p) // x 可以用 *p 訪問
- 運算符,也稱為解引用運算符,用于訪問地址中的值。
&運算符,也稱為地址運算符,用于返回變量的地址。
Q3 Go 允許多個返回值嗎?
允許
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("A", "B")
fmt.Println(a, b) // B A
}
Q4 Go 有異常類型嗎?
Go 沒有異常類型,只有錯誤類型(Error),通常使用返回值來表示異常狀態(tài)。
f, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
Q5 什么是協(xié)程(Goroutine)
Goroutine 是與其他函數(shù)或方法同時運行的函數(shù)或方法。 Goroutines 可以被認為是輕量級的線程。 與線程相比,創(chuàng)建 Goroutine 的開銷很小。 Go應用程序同時運行數(shù)千個 Goroutine 是非常常見的做法。
Q6 如何高效地拼接字符串
Go 語言中,字符串是只讀的,也就意味著每次修改操作都會創(chuàng)建一個新的字符串。如果需要拼接多次,應使用 strings.Builder,最小化內(nèi)存拷貝次數(shù)。
var str strings.Builder
for i := 0; i < 1000; i++ {
str.WriteString("a")
}
fmt.Println(str.String())
Q7 什么是 rune 類型
ASCII 碼只需要 7 bit 就可以完整地表示,但只能表示英文字母在內(nèi)的128個字符,為了表示世界上大部分的文字系統(tǒng),發(fā)明了 Unicode, 它是ASCII的超集,包含世界上書寫系統(tǒng)中存在的所有字符,并為每個代碼分配一個標準編號(稱為Unicode CodePoint),在 Go 語言中稱之為 rune,是 int32 類型的別名。
Go 語言中,字符串的底層表示是 byte (8 bit) 序列,而非 rune (32 bit) 序列。例如下面的例子中 語 和 言 使用 UTF-8 編碼后各占 3 個 byte,因此 len(“Go語言”) 等于 8,當然我們也可以將字符串轉換為 rune 序列。
fmt.Println(len("Go語言")) // 8
fmt.Println(len([]rune("Go語言"))) // 4
Q8 如何判斷 map 中是否包含某個 key ?
if val, ok := dict["foo"]; ok {
//do something here
}
dict[“foo”] 有 2 個返回值,val 和 ok,如果 ok 等于 true,則說明 dict 包含 key “foo”,val 將被賦予 “foo” 對應的值。
Q9 Go 支持默認參數(shù)或可選參數(shù)嗎?
Go 語言不支持可選參數(shù)(python 支持),也不支持方法重載(java支持)。
Q10 defer 的執(zhí)行順序
多個 defer 語句,遵從后進先出(Last In First Out,LIFO)的原則,最后聲明的 defer 語句,最先得到執(zhí)行。
defer 在 return 語句之后執(zhí)行,但在函數(shù)退出之前,defer 可以修改返回值。
例如:
func test() int {
i := 0
defer func() {
fmt.Println("defer1")
}()
defer func() {
i += 1
fmt.Println("defer2")
}()
return i
}
func main() {
fmt.Println("return", test())
}
// defer2
// defer1
// return 0
這個例子中,可以看到 defer 的執(zhí)行順序:后進先出。但是返回值并沒有被修改,這是由于 Go 的返回機制決定的,執(zhí)行 return 語句后,Go 會創(chuàng)建一個臨時變量保存返回值,因此,defer 語句修改了局部變量 i,并沒有修改返回值。那如果是有名的返回值呢?
func test() (i int) {
i = 0
defer func() {
i += 1
fmt.Println("defer2")
}()
return i
}
func main() {
fmt.Println("return", test())
}
// defer2
// return 1
這個例子中,返回值被修改了。對于有名返回值的函數(shù),執(zhí)行 return 語句時,并不會再創(chuàng)建臨時變量保存,因此,defer 語句修改了 i,即對返回值產(chǎn)生了影響。
Q11 如何交換 2 個變量的值?
a, b := "A", "B"
a, b = b, a
fmt.Println(a, b) // B A
Q12 Go 語言 tag 的用處?
tag 可以理解為 struct 字段的注解,可以用來定義字段的一個或多個屬性??蚣?工具可以通過反射獲取到某個字段定義的屬性,采取相應的處理方式。tag 豐富了代碼的語義,增強了靈活性。
例如:
package main
import "fmt"
import "encoding/json"
type Stu struct {
Name string `json:"stu_name"`
ID string `json:"stu_id"`
Age int `json:"-"`
}
func main() {
buf, _ := json.Marshal(Stu{"Tom", "t001", 18})
fmt.Printf("%s\n", buf)
}
這個例子使用 tag 定義了結構體字段與 json 字段的轉換關系,Name -> stu_name, ID -> stu_id,忽略 Age 字段。很方便地實現(xiàn)了 Go 結構體與不同規(guī)范的 json 文本之間的轉換。
Q13 如何判斷 2 個字符串切片(slice) 是相等的?
go 語言中可以使用反射 reflect.DeepEqual(a, b) 判斷 a、b 兩個切片是否相等,但是通常不推薦這么做,使用反射非常影響性能。
通常采用的方式如下,遍歷比較切片中的每一個元素(注意處理越界的情況)。
func StringSliceEqualBCE(a, b []string) bool {
if len(a) != len(b) {
return false
}
if (a == nil) != (b == nil) {
return false
}
b = b[:len(a)]
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
Q14 字符串打印時,%v 和 %+v 的區(qū)別
%v 和 %+v 都可以用來打印 struct 的值,區(qū)別在于 %v 僅打印各個字段的值,%+v 還會打印各個字段的名稱。
type Stu struct {
Name string
}
func main() {
fmt.Printf("%v\n", Stu{"Tom"}) // {Tom}
fmt.Printf("%+v\n", Stu{"Tom"}) // {Name:Tom}
}
但如果結構體定義了 String() 方法,%v 和 %+v 都會調(diào)用 String() 覆蓋默認值。
Q15 Go 語言中如何表示枚舉值(enums)
通常使用常量(const) 來表示枚舉值。
type StuType int32
const (
Type1 StuType = iota
Type2
Type3
Type4
)
func main() {
fmt.Println(Type1, Type2, Type3, Type4) // 0, 1, 2, 3
}
參考 What is an idiomatic way of representing enums in Go? - StackOverflow
Q16 空 struct{} 的用途
使用空結構體 struct{} 可以節(jié)省內(nèi)存,一般作為占位符使用,表明這里并不需要一個值。
fmt.Println(unsafe.Sizeof(struct{}{})) // 0
比如使用 map 表示集合時,只關注 key,value 可以使用 struct{} 作為占位符。如果使用其他類型作為占位符,例如 int,bool,不僅浪費了內(nèi)存,而且容易引起歧義。
type Set map[string]struct{}
func main() {
set := make(Set)
for _, item := range []string{"A", "A", "B", "C"} {
set[item] = struct{}{}
}
fmt.Println(len(set)) // 3
if _, ok := set["A"]; ok {
fmt.Println("A exists") // A exists
}
}
再比如,使用信道(channel)控制并發(fā)時,我們只是需要一個信號,但并不需要傳遞值,這個時候,也可以使用 struct{} 代替。文章來源:http://www.zghlxwxcb.cn/news/detail-632592.html
func main() {
ch := make(chan struct{}, 1)
go func() {
<-ch
// do something
}()
ch <- struct{}{}
// ...
}
再比如,聲明只包含方法的結構體。文章來源地址http://www.zghlxwxcb.cn/news/detail-632592.html
type Lamp struct{}
func (l Lamp) On() {
println("On")
}
func (l Lamp) Off() {
println("Off")
}
到了這里,關于Go 語言面試題(一):基礎語法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!