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


Golang基礎題

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

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

輸出:
打印后
打印中
打印前
panic: 觸發異常
......

解析:協程遇到panic時,遍歷本協程的defer鏈表(defer 是一個鏈表的結構,新的defer會被追加到defer鏈表的最前面,可理解為后進先出),並執行defer。在執行defer過程中,遇到recover則停止panic,返回recover處繼續往下執行。如果沒有遇到recover,遍歷完本協程的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

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

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

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

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函數創建切片時,如果有指定長度,則分配一個指定長度的數組,初始化值為0,並返回一個引用該數組的slice

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

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

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

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

解析:new 和 make的區別:

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

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 類型的指針,不能對指針執行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一直保持自增狀態

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寫數據時,<- 右端必須要有值

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) 為函數傳入可變參數的表達式。...type本質上是一個數組切片,也就是[]type,切片作為函數參數傳的是指針拷貝,具體的坑點可參考文章:(正經版)面試官:切片作為函數參數是傳值還是傳引用?

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

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

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

A. array

B. slice

C. map

D. channel

答案:ABD

解析:cap()函數只支持 數組(array),數組指針(*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;整數的零值為 0;浮點數的零值為 0.0;string 的零值為 空字符串 ""

20、下面代碼輸出什么?

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

答案:0

解析:使用內置函數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 必須在這之前就已經聲明

23、下面代碼輸出什么?

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

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

    i += 10
}

答案:5

解析:f() 函數的參數在執行defer語句的時候會保存一份副本,在實際調用f()函數時使用,所以是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、關於可變參數的函數調用正確的是?

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

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

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編碼中,十進制數字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

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

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坑坑窪窪的使用細節你mark了嗎

30、函數 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坑坑窪窪的使用細節你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作為函數參數,相當於變量拷貝,這里是指針變量的拷貝,指針變量的值依然是指向原有變量的地址,因此最終會輸出29
輸出1作為函數參數,在 defer 定義時就把值傳遞給 defer 函數體緩存起來,因此輸出28

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

A. var a []int

B. a := []int{}

答案:A

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

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 會編輯錯誤

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

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]

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

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坑坑窪窪的使用細節你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 實現了接口 Speak,並不是類型Student,
但是如果是 Student 類型實現了 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) 中並非是首行出現,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

解析:當且僅當動態值和動態類型都為 nil 時,接口類型值才為 nil。上面的代碼,給變量 p 賦值之后,
p 的動態值是 nil,但是動態類型卻是 *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時就會自動去執行 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 函數會發生 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 循環進去了

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 在每次循環體中都會被重用,而不是重新聲明。詳細可參考文章:Golang 之 我被 for-range 循環進去了

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

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 循環進去了

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]

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

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、關於協程,下列說法正確的是

A. 協程和線程都可以實現程序的並發執行;

B. 線程比協程更輕量級;

C. 協程不存在死鎖問題;

D. 通過 channel 來進行協程間的通信;

答案:AD

53、關於循環語句,下面說法正確的是

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

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

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

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

答案:CD

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

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

D:for 語句執行變量平衡初始化

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運算符優先級:
(+ 、-)> (=、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|=)

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

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

B. 單個 case 中,可以出現多個結果選項;

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

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

答案:BD

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

56、下列 Add 函數定義正確的是:

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()函數的參數應該是 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、下面代碼是否會觸發異常

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 會隨機選擇一個可用通道做收發操作

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

A. 給一個 nil channel 發送數據,造成永遠阻塞

B. 從一個 nil channel 接收數據,造成永遠阻塞

C. 給一個已經關閉的 channel 發送數據,引起 panic

D. 從一個已經關閉的 channel 接收數據,如果緩沖區中為空,則返回一個零值

答案:ABCD

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

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

const i = 100
var j = 123

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

答案:編譯失敗:cannot 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

解析:
接口除了有靜態類型,還有動態類型和動態值,
當且僅當動態值和動態類型都為 nil 時,接口類型值才為 nil。
這里的 x 的動態類型是 *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)
}

答案:編譯失敗:invalid 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 循環時,首先會對循環對象的長度進行拷貝,循環的次數取決於拷貝的值,該拷貝值不受循環對象在循環過程中長度變化的影響

72、下面代碼有什么問題

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

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

答案:編譯失敗:panic: assignment to entry in nil map

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

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

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM