Golang 基礎面試高頻題總匯【第一版】來啦~

Golang基礎題

1、下面這段代碼的輸出什么?

func main() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()
    panic("觸發(fā)異常")
}

輸出:
打印后
打印中
打印前
panic: 觸發(fā)異常
......

解析:協(xié)程遇到panic時,遍歷本協(xié)程的defer鏈表(defer 是一個鏈表的結構,新的defer會被追加到defer鏈表的最前面,可理解為后進先出),并執(zhí)行defer。在執(zhí)行defer過程中,遇到recover則停止panic,返回recover處繼續(xù)往下執(zhí)行。如果沒有遇到recover,遍歷完本協(xié)程的defer鏈表后,向stderr拋出panic信息

2、下面代碼會輸出什么?

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
}

輸出:
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4

解析:這里需要注意的是,當執(zhí)行到defer定義時,首先會對參數(shù)進行求值,然后參數(shù)被壓入函數(shù)調用棧,此時不會進入defer函數(shù)體,而是直到函數(shù)返回時才調用defer函數(shù)體。參數(shù)被壓入函數(shù)調用棧時,如果參數(shù)是值類型,那么將復制值,如果參數(shù)是指針,那么將復制指針而不是復制指針指向的值。

3、下面代碼輸出什么?

func main() {
    slice := []int{0, 1, 2, 3}
    m := make(map[int]*int)

    for key, val := range slice {
        m[key] = &val
    }

    for k, v := range m {
        fmt.Printf("key: %d, value: %d \n", k, *v)
    }
}

輸出:
key: 0, value: 3
key: 1, value: 3
key: 2, value: 3
key: 3, value: 3

解析:整個循環(huán)中,val都是同一個全局變量(與for range 結構有關),地址是一樣的,每次遍歷該地址上的值都會被新的值覆蓋掉,所以在遍歷結束后,該地址存的值是切片上的最后一個元素3。具體for range 坑點可以參考這篇文章Golang 之 我被 for-range 循環(huán)進去了

4、下面代碼會輸出什么

func main() {
    i := make([]int, 5)
    i = append(i, 1, 2, 3)
    fmt.Println(i)

    j := make([]int, 0)
    j = append(j, 1, 2, 3, 4)
    fmt.Println(j)
}

輸出:
[0 0 0 0 0 1 2 3]
[1 2 3 4]

解析:make函數(shù)創(chuàng)建切片時,如果有指定長度,則分配一個指定長度的數(shù)組,初始化值為0,并返回一個引用該數(shù)組的slice

5、下面這段代碼有錯誤嗎?

func funcMui(x,y int)(sum int,error){
    return x+y,nil
}

解析:第二個返回值沒有命名,在函數(shù)有多個返回值時,只要有一個返回值有命名,其他的也必須命名。如果有多個返回值必須加上括號 ();如果只有一個返回值且命名也必須加上括號 ()。這里的第一個返回值有命名 sum,第二個沒有命名,所以錯誤。

6、new() 和 make() 的區(qū)別

解析:new 和 make的區(qū)別:

  • new 返回的是指向類型的指針,即類型 *Type;make 返回的還是引用類型本身;
  • new 分配的空間被清零,也就是類型的零值(int類型的零值是0,string類型的零值是"",引用類型的零值是nil)。make 分配空間后,會進行初始化;
  • new 可以分配任意類型的數(shù)據(jù),make 只能用來分配及初始化類型為 slice、map、chan 的數(shù)據(jù)

7、下面代碼能否編譯通過,如果能,輸出什么?

func main() {
    list := new([]int)
    list = append(list, 1)
    fmt.Println(list)

    s1 := []int{1, 2, 3}
    s2 := []int{4, 5}
    s1 = append(s1, s2)
    fmt.Println(s1)
}

> 解析:編譯錯誤

首先 new([]int) 之后的 list 是一個 *[]int 類型的指針,不能對指針執(zhí)行append操作;
再看 s1 = append(s1, s2),編譯失敗,追加一個切片,切片需要解包,正確姿勢是:s1 = append(s1, s2...)

8、下面代碼能否編譯通過

func Test7(t *testing.T) {
    sn1 := struct {
        age  int
        name string
    }{age: 11, name: "qq"}
    sn2 := struct {
        age  int
        name string
    }{age: 11, name: "qq"}
    
    // 可以比較
    if sn1 == sn2 {
        fmt.Println("sn1 == sn2")
    }

    sm1 := struct {
        age int
        m   map[string]string
    }{age: 11, m: map[string]string{"a": "1"}}
    sm2 := struct {
        age int
        m   map[string]string
    }{age: 11, m: map[string]string{"a": "1"}}
    // 編譯錯誤,map不能進行比較
    if sm1 == sm2 {
        fmt.Println("sm1 == sm2")
    }
}

解析:
詳細解析可參考文章:golang之struct能不能比較

9、通過指針變量 p 訪問其成員變量 name,有哪幾種方式?

A. p.name

B. (&p).name

C. (*p).name

D. p->name

答案:AC

10、關于字符串連接,下面語法正確的是

A. str := 'abc' + '123'

B. str := "abc" + "123"

C. str := '123' + "abc"

D. fmt.Sprintf("abc%d", 123)

答案:BD

11、關于iota,下面代碼會輸出什么?

func main() {
    const (
        x = iota
        _
        i
        j
        y
        z = "aa"
        o
        k = iota
        p
        q
    )
    fmt.Println(x,i,j, y, z,o, k, p, q)
}

輸出:
0
2
3
4
aa
aa
7
8
9

解析:iota 初始值為0,往下進行自增1 進行賦值,遇到自定義賦值的常量,則對當前常量進行自定義賦值,后面常量的值跟當前常量的值一致,直到又遇到自定義賦值的常量,在這個過程中iota一直保持自增狀態(tài)

12、下面賦值正確的是

A. var x = nil

B. var x interface{} = nil

C. var x string = nil

D. var x error = nil

答案:BD

13、關于channel,下面語法正確的是?

A. var ch chan int

B. ch := make(chan int)

C. <- ch

D. ch <-

答案:ABC

解析:往channel寫數(shù)據(jù)時,<- 右端必須要有值

14、下面代碼輸出什么?

func hello (num ...int) {
    num[0] = 18
}

func main() {
    i := []int{5, 6, 7}
    hello(i...)
    fmt.Println(i[0])
}
A.18

B.5

C.Compilation error

答案:18

解析:func hello (num ...int) 為函數(shù)傳入可變參數(shù)的表達式。...type本質上是一個數(shù)組切片,也就是[]type,切片作為函數(shù)參數(shù)傳的是指針拷貝,具體的坑點可參考文章:(正經(jīng)版)面試官:切片作為函數(shù)參數(shù)是傳值還是傳引用?

15、下面選擇哪個?

func main() {  
    a := 5
    b := 8.1
    fmt.Println(a + b)
}

A.13.1

B.13

C.compilation error

答案:C

解析:整型和浮點型不能相加

16、下面代碼輸出什么?

func Test15(t *testing.T) {
    a := [5]int{1, 2, 3, 4, 5}
    s := a[3:4:4]
    fmt.Println(s[0])
}

答案:4

解析:a[low : high : max],其中由 a[low : high] 生成切片,切片的容量等于(max - low)

17、下面代碼輸出什么?

func main() {
    a := [2]int{5, 6}
    b := [3]int{5, 6}
    if a == b {
        fmt.Println("equal")
    } else {
        fmt.Println("not equal")
    }
}

A. compilation error

B. equal

C. not equal

答案:A

解析:數(shù)組的比較的前提是兩者要有相同的長度

18、下列哪個類型可以使用cap() 函數(shù)

A. array

B. slice

C. map

D. channel

答案:ABD

解析:cap()函數(shù)只支持 數(shù)組(array),數(shù)組指針(*array),切片(slice),通道(channel)

19、下面代碼輸出什么?

func main() {
    var i interface{}
    if i == nil {
        fmt.Println("nil")
        return
    }
    fmt.Println("not nil")
}

答案:nil

解析:引用類型的零值為 nil, 常見的引用類型有: point, function, interface, slice, channel, map
bool 的零值為 false;整數(shù)的零值為 0;浮點數(shù)的零值為 0.0;string 的零值為 空字符串 ""

20、下面代碼輸出什么?

func main() {
    s := make(map[string]int)
    delete(s, "h")
    fmt.Println(s["h"])
}

答案:0

解析:使用內置函數(shù)delete刪除map的鍵值對時,如果map為nil 或者鍵值對不存在時,delete不會做任何操作,程序不會報錯;獲取不存在的鍵值對時,返回值為對應類型的零值,所以返回0

21、下面代碼輸出什么?

func main() {
    i := -5
    j := +5
    fmt.Printf("%+d %+d", i, j)
}

答案:-5 +5

解析:%+d 是帶符號輸出

22、定義一個全局字符串變量,下列正確的是?

A. var str string

B. str := ""

C. str = ""

D. var str = ""

答案:AD

解析:B選項只支持局部變量聲明;C是賦值語句,str 必須在這之前就已經(jīng)聲明

23、下面代碼輸出什么?

func f(i int) {
    fmt.Println(i)
}

func main() {
    i := 5
    defer f(i)

    i += 10
}

答案:5

解析:f() 函數(shù)的參數(shù)在執(zhí)行defer語句的時候會保存一份副本,在實際調用f()函數(shù)時使用,所以是5

24、下面代碼輸出什么?

func main() {
    str := "hello"
    str[0] = 'x'
    fmt.Println(str)
}

A. hello

B. xello

C. compilation error

答案:C 編譯錯誤

解析:Golang中,字符串是只讀的,可以打印,如 fmt.Println(str[0]),不可修改

25、關于可變參數(shù)的函數(shù)調用正確的是?

func add(args ...int) int {

    sum := 0
    for _, arg := range args {
        sum += arg
    }
    return sum
}

A. add(1, 2)

B. add(1, 3, 7)

C. add([]int{1, 2})

D. add([]int{1, 3, 7}…)

答案:ABD

解析:可變參數(shù)如果是切片,必須是 []type{}... 格式,別問,問就是規(guī)定的

26、下面代碼填入哪個變量會輸出 yes nil

func main() {
    var s1 []int
    var s2 = []int{}
    if ___ == nil {
        fmt.Println("yes nil")
    } else {
        fmt.Println("no nil")
    }
}

答案:s1

解析:s1 表示nil切片(切片的零值)nil 切片和 nil 相等;s2 表示空切片,表示一個空的集合,和nil不相等

27、下面代碼會輸出什么?

func main() {
    i := 65
    fmt.Println(string(i))
}

A. A

B. 65

C. compilation error

答案:A

解析:UTF-8編碼中,十進制數(shù)字65對應的符號是A

28、切片 a,b,c 的容量分別是多少?

func main() {
    s := [3]int{1, 2, 3}
    a := s[:0]
    b := s[:2]
    c := s[1:2:cap(s)]
    fmt.Println(cap(a))
    fmt.Println(cap(b))
    fmt.Println(cap(c))
}

答案:
3
3
2

解析:數(shù)組的容量等于數(shù)據(jù)的長度,s[a:b:c] 容量計算可以參考第16題,切片容量計算可參考文章:(正經(jīng)版)面試官:切片作為函數(shù)參數(shù)是傳值還是傳引用?

29、下面代碼輸出什么?

func increaseA() int {
    var i int
    defer func() {
        i++
    }()
    return i
}

func increaseB() (r int) {
    defer func() {
        r++
    }()
    return r
}

func main() {
    fmt.Println(increaseA())
    fmt.Println(increaseB())
}

答案:
0
1

解析:參考文章:關于defer坑坑洼洼的使用細節(jié)你mark了嗎

30、函數(shù) f1(),f2(),f3() 分別返回什么?

func f1() (r int) {
    defer func() {
        r++
    }()
    return 0
}

func f2() (r int) {
    t := 5
    defer func() {
        t = t + 5
    }()
    return t
}

func f3() (r int) {
    defer func(r int) {
        r = r + 5
    }(r)
    return 1
}

func main() {
    fmt.Println(f1())
    fmt.Println(f2())
    fmt.Println(f3())
}

答案:
1
5
1

解析:參考文章:關于defer坑坑洼洼的使用細節(jié)你mark了嗎

31、下面代碼輸出什么?

type Person struct {
    age int
}

func main() {
    person := &Person{28}

    defer fmt.Printf("輸出1:%v\n", person.age)

    defer func(p *Person) {
        fmt.Printf("輸出2:%v\n", p.age)
    }(person)

    defer func() {
        fmt.Printf("輸出3:%v\n", person.age)
    }()

    person.age = 29
}

答案:
輸出3:29
輸出2:29
輸出1:28

解析:
輸出3作為閉包引用,所以輸出29
輸出2作為函數(shù)參數(shù),相當于變量拷貝,這里是指針變量的拷貝,指針變量的值依然是指向原有變量的地址,因此最終會輸出29
輸出1作為函數(shù)參數(shù),在 defer 定義時就把值傳遞給 defer 函數(shù)體緩存起來,因此輸出28

32、下面的兩個切片聲明中有上面區(qū)別?哪個更可???

A. var a []int

B. a := []int{}

答案:A

解析:
A 聲明的是 nil 切片;
B 聲明的是長度和容量都為 0 的空切片。
A 的聲明不會分配內存,優(yōu)先選擇

33、A、B、C、D 哪個有語法錯誤

type S struct {
}

func m(x interface{}) {
}

func g(x *interface{}) {
}

func main() {
    s := S{}
    p := &s
    m(s) //A
    g(s) //B
    m(p) //C
    g(p) //D
}

答案:B、D 會編輯錯誤

解析:函數(shù)參數(shù)為 interface {} 時可以接收任何類型的參數(shù),包括用戶自定義類型等,
即使是接收指針類型也用 interface {},而不是使用 *interface {}
永遠不要使用一個指針指向一個接口類型,因為它已經(jīng)是一個指針。

34、下面代碼輸出什么?

func main() {
    s1 := []int{1, 2, 3}
    s2 := s1[1:]
    s2[1] = 4
    fmt.Println(s1)
    s2 = append(s2, 5, 6, 7)
    fmt.Println(s1)
}

答案:
[1 2 4]
[1 2 4]

解析:相信看過大叔之前文章的同學一下就能回答上來,不贅述,附上文章鏈接:(正經(jīng)版)面試官:切片作為函數(shù)參數(shù)是傳值還是傳引用?

35、下列代碼輸出什么?

func main() {
    if a := 1; false {
        
    } else if b := 2; false {
    
    } else {
        fmt.Println(a, b)
    }
}

答案:1 2

解析:if 語句聲明變量,該變量的作用域只屬于整個 if block 結構

36、下面代碼輸出什么

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("A", a, calc("10", a, b))
    a = 0
    defer calc("B", a, calc("20", a, b))
    b = 1
}

答案:
10 1 2 3
20 0 2 2
B 0 2 2
A 1 3 4

解析:參考文章:關于defer坑坑洼洼的使用細節(jié)你mark了嗎

37、下列代碼輸出什么

func main() {
    m := map[int]string{0: "zero", 1: "one"}
    for k, v := range m {
        fmt.Println(k, v)
    }
}

答案:
1 one
0 zero

0 zero
1 one

解析:map 輸出是無序的

38、下面代碼能否正常打印輸出

type People interface {
    Speak(string)
}

type Student struct{}

func (stu *Student) Speak(think string) {
    fmt.Println(think)
}

func main() {
    var peo People 
    student := Student{}
    peo = student
    
    peo.Speak("speak")
}

答案:編譯失敗,Student does not implement People (Speak method has pointer receiver)

解析:
因為是 *Student 實現(xiàn)了接口 Speak,并不是類型Student,
但是如果是 Student 類型實現(xiàn)了 Speak 方法,那么用 值類型的 Student{} 或是指針類型的 &Student{} 都可以訪問到該方法。

39、下面代碼輸出什么

const (
    a = iota
    b = iota
)
const (
    name = "name"
    c    = iota
    d    = iota
)

func main() {
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}

答案:0 1 1 2

解析:
1、每個常量塊(const block) 結構體都會重置和初始化 iota 的值為 0,往后常量塊中每新增一行常量聲明 iota將自增 1
2、即便 iota 在常量塊(const block) 中并非是首行出現(xiàn),iota 也會在首行進行初始化(值為 0),完后常量進行自增 1

40、下面代碼輸出什么?

func main() {
    const a = iota              
    const b = iota + 3          
    const c, d = iota, iota + 3 
    const (
        e = iota     // e=0
        f = iota + 4 // f=5
        g            // g=6
    )
    println(a, b, c, d, e, f, g)
}

答案:0 3 0 3 0 5 6

解析:參考38題

41、下面代碼輸出什么?

type People interface {
    Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func main() {
    var s *Student
    if s == nil {
        fmt.Println("s is nil")
    } else {
        fmt.Println("s is not nil")
    }
    var p People = s
    if p == nil {
        fmt.Println("p is nil")
    } else {
        fmt.Println("p is not nil")
    }
}

答案:
s is nil
p is not nil

解析:當且僅當動態(tài)值和動態(tài)類型都為 nil 時,接口類型值才為 nil。上面的代碼,給變量 p 賦值之后,
p 的動態(tài)值是 nil,但是動態(tài)類型卻是 *Student,是一個 nil 指針,所以相等條件不成立。

42、下面代碼輸出什么

type Direction int

const (
    North Direction = iota
    East
    South
    West
)

func (d Direction) String() string {
    return [...]string{"North", "East", "South", "West"}[d]
}

func main() {
    fmt.Println(South)
}

答案:South

解析:
在go中如果重寫了String 方法,那在調用fmt.Println時就會自動去執(zhí)行 String 方法

43、下面代碼能夠正常輸出嗎

type Square struct {
    x, y int
}

var m = map[string]Square{
    "foo": Square{2, 3},
}

func main() {
    m["foo"].x = 1
    fmt.Println(m["foo"].x)
}

答案:編譯失敗

解析:
對于類似 X = Y 的賦值操作,必須知道 X 的地址,才能夠將 Y 的值賦給 X,
但 go 中的 map 的 value 本身是不可尋址的。
正確的寫法有兩種:
第一種:

square := m["foo"]
square.x = 1

這種寫法無法修改 m["foo"] 中 x 變量的值> 第二種:

var m = map[string]*Square{
   "foo": &Square{2, 3},
}
m["foo"].x = 1

這種可以修改 m["foo"] 中 x 變量的值

44、下面代碼輸出什么

var p *int

func foo() (*int, error) {
    var i int = 5
    return &i, nil
}

func bar() {
    //use p
    fmt.Println(*p)
}

func main() {
    p, err := foo()
    if err != nil {
        fmt.Println(err)
        return
    }
    bar()
    fmt.Println(*p)
}

答案:bar 函數(shù)會發(fā)生 panic,空指針異常

解析:因為 err 前面沒有聲明,所以 p, err := foo () 中的 p 是重新聲明的局部變量,而不是我們在前面聲明的全局變量 p

45、下面代碼輸出什么

func main() {
    v := []int{1, 2, 3}
    for i := range v {
        v = append(v, i)
        fmt.Println(v)
    }
}

答案:
[1 2 3 0]
[1 2 3 0 1]
[1 2 3 0 1 2]

解析:可參考文章:Golang 之 我被 for-range 循環(huán)進去了

46、下面代碼會輸出什么?

func main() {
    var m = [...]int{1, 2, 3}

    for i, v := range m {
        go func() {
            fmt.Println(i, v)
        }()
    }

    time.Sleep(time.Second * 1)
}

答案:
2 3
2 3
2 3\

解析:for range 使用短變量聲明 (:=) 的形式迭代變量,
需要注意的是,變量 i、v 在每次循環(huán)體中都會被重用,而不是重新聲明。詳細可參考文章:Golang 之 我被 for-range 循環(huán)進去了

47、下面代碼會輸出什么?

func test(n int) (r int) {
    // 第一個defer
    defer func() {
        r += n
        recover()
    }()

    var f func()
    // 第二個defer
    defer f()
    f = func() {
        r += 2
    }
    return n + 1
}

func main() {
    fmt.Println(test(3))
}

答案:7
解析:
首先,defer 是和返回值被命名的函數(shù)一起使用的,而且 defer 語句定義時對外部變量引用的方式是閉包引用(細節(jié)可參考文章:關于defer坑坑洼洼的使用細節(jié)你mark了嗎,建議先看完文章再往下看,否則有的同學可能會有點接不上)
為什么第二個 defer 不起作用呢?其實第二個 defer 的延遲函數(shù)啥都沒干,甚至程序執(zhí)行到 f() 時還拋出 panic 了。我們都知道 程序在執(zhí)行到 defer 定義時并不會馬上執(zhí)行,而是對捕捉到的 defer 結果進行鏈表構建,把關聯(lián)的函數(shù)入棧緩存,在程序 return 前再進行出棧執(zhí)行。那么問題就很明顯了,我們看在程序執(zhí)行到 defer f() 時,此時的 f 是啥?此時的 f 的值為 nil,所以最后出棧執(zhí)行一個nil 函數(shù),就相當于:

var f func()
f()    // panic: runtime error: invalid memory address or nil pointer dereference

因此這也是為什么第一個 defer 為什么要有 recover() 方法。后面的 f = func() { r += 2 } 是賦值操作,對返回值 r 沒影響

48、下面代碼輸出什么

func test(n int) (r int) {
    // 第一個defer
    defer func() {
        r += n
    }()

    var f func()
    f = func() {
        r += 2
    }
    // 第二個defer
    defer f()
    return n + 1
}

func main() {
    fmt.Println(test(3))
}

答案:9
解析:參考47題

49、下面代碼輸出什么

func main() {
    var a = [5]int{1, 2, 3, 4, 5}
    var r [5]int

    for i, v := range a {
        if i == 0 {
            a[1] = 12
            a[2] = 13
        }
        r[i] = v
    }
    fmt.Println("r = ", r)
    fmt.Println("a = ", a)
}

答案:
r = [1 2 3 4 5]
a = [1 12 13 4 5]\

解析:參考文章:Golang 之 我被 for-range 循環(huán)進去了

50、下面代碼輸出什么?

func main() {
    slice := make([]int, 5, 5)
    slice[0] = 1
    slice[1] = 2
    change(slice...)
    fmt.Println(slice)
    change(slice[0:2]...)
    fmt.Println(slice)
}

答案:
[1 2 0 0 0]
[1 2 3 0 0]

解析:參考文章:(正經(jīng)版)面試官:切片作為函數(shù)參數(shù)是傳值還是傳引用?

51、下面代碼輸出什么

func main() {
    var m = map[string]int{
        "A": 21,
        "B": 22,
        "C": 23,
    }
    counter := 0
    for k, v := range m {
        if counter == 0 {
            delete(m, "A")
        }
        counter++
        fmt.Println(k, v)
    }
    fmt.Println("counter is ", counter)
}

答案:
counter is 2 或者 counter is 3
for range map 是無序的

52、關于協(xié)程,下列說法正確的是

A. 協(xié)程和線程都可以實現(xiàn)程序的并發(fā)執(zhí)行;

B. 線程比協(xié)程更輕量級;

C. 協(xié)程不存在死鎖問題;

D. 通過 channel 來進行協(xié)程間的通信;

答案:AD

53、關于循環(huán)語句,下面說法正確的是

A. 循環(huán)語句既支持 for 關鍵字,也支持 while 和 do-while;

B. 關鍵字 for 的基本使用方法與 C/C++ 中沒有任何差異;

C. for 循環(huán)支持 continue 和 break 來控制循環(huán),但是它提供了一個更高級的 break,可以選擇中斷哪一個循環(huán);

D. for 循環(huán)不支持以逗號為間隔的多個賦值語句,必須使用平行賦值的方式來初始化多個變量;

答案:CD

解析:
C : golang中,goto語句可以無條件地轉移到過程中指定的行,通常與條件語句配合使用。可用來實現(xiàn)條件轉移, 構成循環(huán),跳出循環(huán)體等功能。例如:

    func main() {
        for a:=0;a<5;a++{
            fmt.Println(a)
            if a>3{
                goto Loop
            }
        }
        Loop:           //放在for后邊
        fmt.Println("test")
    }

D:for 語句執(zhí)行變量平衡初始化

for i, j := 0, 5; i < j; i, j = i+1, j-1 {
      fmt.Println("hhh")
}

54、下面代碼輸出什么?

func main() {
    i := 1
    s := []string{"A", "B", "C"}
    i, s[i-1] = 2, "Z"
    fmt.Printf("s: %v \n", s)
}

答案:s: [Z B C]

解析:golang運算符優(yōu)先級:
(+ 、-)> (=、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|=)

55、關于 switch 語句,下面說法正確的是

A. 條件表達式必須為常量或者整數(shù);

B. 單個 case 中,可以出現(xiàn)多個結果選項;

C. 需要用 break 來明確退出一個 case;

D. 只有在 case 中明確添加 fallthrough 關鍵字,才會繼續(xù)執(zhí)行緊跟的下一個 case;

答案:BD

解析:
switch/case 后是一個表達式(即:常量,變量,一個有返回的函數(shù)都可以);A 錯誤
無需用一個 break 來明確退出一個 case,B 錯

56、下列 Add 函數(shù)定義正確的是:

func Test54(t *testing.T) {
    var a Integer = 1
    var b Integer = 2
    var i interface{} = &a
    sum := i.(*Integer).Add(b)
    fmt.Println(sum)
}

A.
type Integer int
func (a Integer) Add(b Integer) Integer {
        return a + b
}

B.
type Integer int
func (a Integer) Add(b *Integer) Integer {
        return a + *b
}

C.
type Integer int
func (a *Integer) Add(b Integer) Integer {
        return *a + b
}

D.
type Integer int
func (a *Integer) Add(b *Integer) Integer {
        return *a + *b
}

答案:AC

解析:Add()函數(shù)的參數(shù)應該是 Integer 類型,BD錯誤

57、關于 bool 變量 b 的賦值,下面錯誤的用法是?

A. b = true

B. b = 1

C. b = bool(1)

D. b = (1 == 2)

答案:BC

解析:
golang 中 bool 類型變量的值只能是 true / false,B 錯誤
golang中沒有 bool(1) 這種強制轉換的表達方式,C 錯誤

58、關于變量的自增和自減操作,下面語句正確的是:

A
i := 1
i++

B
i := 1
j = i++

C
i := 1
++i

D
i := 1
i--

答案:AD

解析:golang 中沒有 ++i 或者 --i這種表達式,B 中 新變量 j 的聲明和初始化應該使用 :=

59、關于 GetPodAction 定義,下面賦值正確的是

type Fragment interface {
        Exec(transInfo *TransInfo) error
}
type GetPodAction struct {
}
func (g GetPodAction) Exec(transInfo *TransInfo) error {
        ...
        return nil
}

A. var fragment Fragment = new(GetPodAction)

B. var fragment Fragment = GetPodAction

C. var fragment Fragment = &GetPodAction{}

D. var fragment Fragment = GetPodAction{}

答案:ACD

60、關于整型切片的初始化,下面正確的是

A. s := make([]int)

B. s := make([]int, 0)

C. s := make([]int, 5, 10)

D. s := []int{1, 2, 3, 4, 5}

答案:BCD

解析:使用內置方法 make() 初始化切片時必須指定長度

61、下面代碼是否會觸發(fā)異常

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

答案:可能會,可能不會

解析:當兩個 chan 同時有值時,即 select 中的 case 都匹配時,select 會隨機選擇一個可用通道做收發(fā)操作

62、關于 channel 的特性,下面說法正確的是

A. 給一個 nil channel 發(fā)送數(shù)據(jù),造成永遠阻塞

B. 從一個 nil channel 接收數(shù)據(jù),造成永遠阻塞

C. 給一個已經(jīng)關閉的 channel 發(fā)送數(shù)據(jù),引起 panic

D. 從一個已經(jīng)關閉的 channel 接收數(shù)據(jù),如果緩沖區(qū)中為空,則返回一個零值

答案:ABCD

解析:概念性的知識點玩命記就是了

63、下面代碼有什么問題:

const i = 100
var j = 123

func main() {
    fmt.Println(&j, j)
    fmt.Println(&i, i)
}

答案:編譯失?。篶annot take the address of i

解析:golang 中,常量無法尋址,是不能進行做指針操作的

64、下面代碼輸出什么?

func main() {
    x := []string{"a", "b", "c"}
    for v := range x {
        fmt.Print(v)
    }
}

答案:012

解析:range 一個返回值時,這個值是下標,兩個值時,第一個是下標,第二個是值,當 x 為 map 時,第一個是 key,第二個是 value

65、關于無緩沖和有緩沖的 channel,下面說法正確的是?

A. 無緩沖的 channel 是默認的緩沖為 1 的 channel;

B. 無緩沖的 channel 和有緩沖的 channel 都是同步的;

C. 無緩沖的 channel 和有緩沖的 channel 都是非同步的;

D. 無緩沖的 channel 是同步的,而有緩沖的 channel 是非同步的;

答案:D

66、下面代碼輸出什么?

func Foo(x interface{}) {
    if x == nil {
        fmt.Println("empty interface")
        return
    }
    fmt.Println("non-empty interface")
}
func main() {
    var x *int = nil
    Foo(x)
}

答案:non-empty interface

解析:
接口除了有靜態(tài)類型,還有動態(tài)類型和動態(tài)值,
當且僅當動態(tài)值和動態(tài)類型都為 nil 時,接口類型值才為 nil。
這里的 x 的動態(tài)類型是 *int,所以 x 不為 nil

67、關于 select 機制,下面說法正確的是

A. select 機制用來處理異步 IO 問題;

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

C. golang 在語言級別支持 select 關鍵字;

D. select 關鍵字的用法與 switch 語句非常類似,后面要帶判斷條件;

答案:ABC

解析:
select后不用帶判斷條件,是case后帶判斷條件。
select的用法與switch非常類似,由select開始一個新的選擇塊,每個選擇條件由case語句來描述。與switch語句可以選擇任何可使用相等比較的條件相比,select有比較多的限制,其中最大的一條限制就是每個case語句里必須是一個IO操作,確切的說,應該是一個面向channel的IO操作。

68、下面代碼有什么問題

func Stop(stop <-chan bool) {
        close(stop)
}
func main() {
    c := make(chan bool)
    Stop(c)
}

答案:編譯失?。篿nvalid operation: close(stop) (cannot close receive-only channel)

解析:從輸出的錯誤信息也可以知道,有方向的 channel 不可以被關閉

69、下列代碼輸出什么

func main() {
    var x = []int{2: 2, 3, 0: 1}
    fmt.Println(x)
}

答案:[1, 0, 2, 3]

解析:切片初始化特性如下:

var x = []int{2: 2, 3, 0: 1, 1}      // [1 1 2 3]
var x = []int{2: 2, 3, 0: 1, 4:4}    // [1 0 2 3 4]
var x = []int{2: 2, 3, 0: 1, 5:4}    // [1 0 2 3 0 4]

70、下面代碼輸出什么

func incr(p *int) int {
    *p++
    return *p
}
func main() {
    v := 1
    incr(&v)
    fmt.Println(v)
}

答案:2

71、下面代碼輸出什么

func main() {
    var a = []int{1, 2, 3, 4, 5}
    var r = make([]int, 0)

    for i, v := range a {
        if i == 0 {
            a = append(a, 6, 7)
        }
        r = append(r, v)
    }
    fmt.Println(r)
}

答案:[1,2,3,4,5]

解析:for range 循環(huán)時,首先會對循環(huán)對象的長度進行拷貝,循環(huán)的次數(shù)取決于拷貝的值,該拷貝值不受循環(huán)對象在循環(huán)過程中長度變化的影響

72、下面代碼有什么問題

func main() {
    var s []int
    s = append(s,1)

    var m map[string]int
    m["one"] = 1 
}

答案:編譯失?。簆anic: assignment to entry in nil map

解析:切片聲明即可使用,但 map 需要用 make 函數(shù) 進行初始化之后才能賦值

73、下面代碼輸出什么

func main() {
    fmt.Println("1")
    defer fmt.Println("in main")
    defer func() {
        fmt.Println("3")
    }()
    fmt.Println("2")
    defer func() {
        defer func() {
            panic("panic again and again")
        }()
        panic("panic again")
    }()
    
    panic("panic once")
}

答案:
1
2
3
in main
panic: panic once
panic: panic again
panic: panic again and again

解析:協(xié)程遇到panic時,遍歷本協(xié)程的defer鏈表,并執(zhí)行defer。在執(zhí)行defer過程中,遇到recover則停止panic,返回recover處繼續(xù)往下執(zhí)行。如果沒有遇到recover,遍歷完本協(xié)程的defer鏈表后,向stderr拋出panic信息,可參考文章 關于defer坑坑洼洼的使用細節(jié)你mark了嗎

關注 公眾號「大叔說碼」 跟大叔一起打基礎,我們下期見~

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容