├── .gitattributes ├── 121-150.md ├── 151-180.md ├── 181-194.md ├── 31-60.md ├── 61-90.md ├── 91-120.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-language=go 2 | -------------------------------------------------------------------------------- /121-150.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [121\. 下面代码输出什么?](#121-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 4 | > * [122\. 下面这段代码输出什么?](#122-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 5 | > * [123\. 下面这段代码输出什么?](#123-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 6 | > * [124\. 下面代码输出什么?](#124-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 7 | > * [125\. 下面的代码能否正确输出?](#125-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E6%AD%A3%E7%A1%AE%E8%BE%93%E5%87%BA) 8 | > * [126\. 下面代码输出什么?](#126-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 9 | > * [127\. 下面的代码有什么问题?](#127-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 10 | > * [128\. 下面代码有什么不规范的地方吗?](#128-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E4%B8%8D%E8%A7%84%E8%8C%83%E7%9A%84%E5%9C%B0%E6%96%B9%E5%90%97) 11 | > * [129\. 关于 channel 下面描述正确的是?](#129-%E5%85%B3%E4%BA%8E-channel-%E4%B8%8B%E9%9D%A2%E6%8F%8F%E8%BF%B0%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 12 | > * [130\. 下面的代码有几处问题?请详细说明。](#130-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E5%87%A0%E5%A4%84%E9%97%AE%E9%A2%98%E8%AF%B7%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E) 13 | > * [131\. 下面的代码有什么问题?](#131-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 14 | > * [132\. 下面的代码输出什么?](#132-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [133\. 关于 channel 下面描述正确的是?](#133-%E5%85%B3%E4%BA%8E-channel-%E4%B8%8B%E9%9D%A2%E6%8F%8F%E8%BF%B0%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 16 | > * [134\. 下面的代码有什么问题?](#134-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 17 | > * [135\. 下面的代码有什么问题?](#135-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 18 | > * [136\. 下面代码输出什么?](#136-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 19 | > * [137\. 下面哪一行代码会 panic,请说明原因?](#137-%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%E4%BC%9A-panic%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 20 | > * [138\. 下面的代码输出什么?](#138-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 21 | > * [139\. 下面的代码输出什么?](#139-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 22 | > * [140\. 下面哪一行代码会 panic,请说明原因?](#140-%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%E4%BC%9A-panic%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 23 | > * [141\. 下面的代码输出什么?](#141-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 24 | > * [142\. 下面哪一行代码会 panic,请说明原因?](#142-%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%E4%BC%9A-panic%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 25 | > * [143\. 下面哪一行代码会 panic,请说明原因?](#143-%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%E4%BC%9A-panic%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 26 | > * [144\. 下面的代码有什么问题?](#144-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 27 | > * [145\. 下面这段代码输出什么?](#145-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 28 | > * [146\. 下面代码输出什么?](#146-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 29 | > * [147\. 下面哪一行代码会 panic,请说明。](#147-%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%E4%BC%9A-panic%E8%AF%B7%E8%AF%B4%E6%98%8E) 30 | > * [148\. 下面代码输出什么?](#148-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 31 | > * [149\. 下面选项正确的是?](#149-%E4%B8%8B%E9%9D%A2%E9%80%89%E9%A1%B9%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 32 | > * [150\. 下面的代码输出什么?](#150-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 33 | > * [151\. 151-180题](https://github.com/yqchilde/Golang-Interview/blob/master/151-180.md) 34 | 35 | ## 121. 下面代码输出什么? 36 | 37 | ```go 38 | func main() { 39 | var ch chan int 40 | select { 41 | case v, ok := <-ch: 42 | println(v, ok) 43 | default: 44 | println("default") 45 | } 46 | } 47 | ``` 48 | 49 | **答:default** 50 | 51 | **解析:** 52 | 53 | ch 为 nil,读写都会阻塞。 54 | 55 | ## 122. 下面这段代码输出什么? 56 | 57 | ```go 58 | type People struct { 59 | name string `json:"name"` 60 | } 61 | 62 | func main() { 63 | js := `{ 64 | "name":"seekload" 65 | }` 66 | var p People 67 | err := json.Unmarshal([]byte(js), &p) 68 | if err != nil { 69 | fmt.Println("err: ", err) 70 | return 71 | } 72 | fmt.Println(p) 73 | } 74 | ``` 75 | 76 | **答:输出 {}** 77 | 78 | **解析:** 79 | 80 | 知识点:结构体访问控制,因为 name 首字母是小写,导致其他包不能访问,所以输出为空结构体。 81 | 82 | 修复代码: 83 | 84 | ```go 85 | type People struct { 86 | Name string `json:"name"` 87 | } 88 | ``` 89 | 90 | ## 123. 下面这段代码输出什么? 91 | 92 | ```GO 93 | type T struct { 94 | ls []int 95 | } 96 | 97 | func foo(t T) { 98 | t.ls[0] = 100 99 | } 100 | 101 | func main() { 102 | var t = T{ 103 | ls: []int{1, 2, 3}, 104 | } 105 | 106 | foo(t) 107 | fmt.Println(t.ls[0]) 108 | } 109 | ``` 110 | 111 | - A. 1 112 | - B. 100 113 | - C. compilation error 114 | 115 | **答:输出 B** 116 | 117 | **解析:** 118 | 119 | 调用 foo() 函数时虽然是传值,但 foo() 函数中,字段 ls 依旧可以看成是指向底层数组的指针。 120 | 121 | ## 124. 下面代码输出什么? 122 | 123 | ```go 124 | func main() { 125 | isMatch := func(i int) bool { 126 | switch(i) { 127 | case 1: 128 | case 2: 129 | return true 130 | } 131 | return false 132 | } 133 | 134 | fmt.Println(isMatch(1)) 135 | fmt.Println(isMatch(2)) 136 | } 137 | ``` 138 | 139 | **答:false true** 140 | 141 | **解析:** 142 | 143 | Go 语言的 switch 语句虽然没有"break",但如果 case 完成程序会默认 break,可以在 case 语句后面加上关键字 fallthrough,这样就会接着走下一个 case 语句(不用匹配后续条件表达式)。或者,利用 case 可以匹配多个值的特性。 144 | 145 | 修复代码: 146 | 147 | ```go 148 | func main() { 149 | isMatch := func(i int) bool { 150 | switch(i) { 151 | case 1: 152 | fallthrough 153 | case 2: 154 | return true 155 | } 156 | return false 157 | } 158 | 159 | fmt.Println(isMatch(1)) // true 160 | fmt.Println(isMatch(2)) // true 161 | 162 | match := func(i int) bool { 163 | switch(i) { 164 | case 1,2: 165 | return true 166 | } 167 | return false 168 | } 169 | 170 | fmt.Println(match(1)) // true 171 | fmt.Println(match(2)) // true 172 | } 173 | ``` 174 | 175 | ## 125. 下面的代码能否正确输出? 176 | 177 | ```go 178 | func main() { 179 | var fn1 = func() {} 180 | var fn2 = func() {} 181 | 182 | if fn1 != fn2 { 183 | println("fn1 not equal fn2") 184 | } 185 | } 186 | ``` 187 | 188 | **答:编译错误** 189 | 190 | ```shell 191 | invalid operation: fn1 != fn2 (func can only be compared to nil) 192 | ``` 193 | 194 | **解析:** 195 | 196 | 函数只能与 nil 比较。 197 | 198 | ## 126. 下面代码输出什么? 199 | 200 | ```go 201 | type T struct { 202 | n int 203 | } 204 | 205 | func main() { 206 | m := make(map[int]T) 207 | m[0].n = 1 208 | fmt.Println(m[0].n) 209 | } 210 | ``` 211 | 212 | - A. 1 213 | - B. compilation error 214 | 215 | **答:B** 216 | 217 | ```shell 218 | cannot assign to struct field m[0].n in map 219 | ``` 220 | 221 | **解析:** 222 | 223 | map[key]struct 中 struct 是不可寻址的,所以无法直接赋值。 224 | 225 | 修复代码: 226 | 227 | ```go 228 | type T struct { 229 | n int 230 | } 231 | 232 | func main() { 233 | m := make(map[int]T) 234 | 235 | t := T{1} 236 | m[0] = t 237 | fmt.Println(m[0].n) 238 | } 239 | ``` 240 | 241 | ## 127. 下面的代码有什么问题? 242 | 243 | ```go 244 | type X struct {} 245 | 246 | func (x *X) test() { 247 | println(x) 248 | } 249 | 250 | func main() { 251 | 252 | var a *X 253 | a.test() 254 | 255 | X{}.test() 256 | } 257 | ``` 258 | 259 | **答:X{} 是不可寻址的,不能直接调用方法** 260 | 261 | **解析:** 262 | 263 | 知识点:在方法中,指针类型的接收者必须是合法指针(包括 nil),或能获取实例地址。 264 | 265 | 修复代码: 266 | 267 | ```go 268 | func main() { 269 | 270 | var a *X 271 | a.test() // 相当于 test(nil) 272 | 273 | var x = X{} 274 | x.test() 275 | } 276 | ``` 277 | 278 | ## 128. 下面代码有什么不规范的地方吗? 279 | 280 | ```go 281 | func main() { 282 | x := map[string]string{"one":"a","two":"","three":"c"} 283 | 284 | if v := x["two"]; v == "" { 285 | fmt.Println("no entry") 286 | } 287 | } 288 | ``` 289 | 290 | **解析:** 291 | 292 | 检查 map 是否含有某一元素,直接判断元素的值并不是一种合适的方式。最可靠的操作是使用访问 map 时返回的第二个值。 293 | 294 | 修复代码如下: 295 | 296 | ```go 297 | func main() { 298 | x := map[string]string{"one":"a","two":"","three":"c"} 299 | 300 | if _,ok := x["two"]; !ok { 301 | fmt.Println("no entry") 302 | } 303 | } 304 | ``` 305 | 306 | ## 129. 关于 channel 下面描述正确的是? 307 | 308 | - A. 向已关闭的通道发送数据会引发 panic; 309 | - B. 从已关闭的缓冲通道接收数据,返回已缓冲数据或者零值; 310 | - C. 无论接收还是接收,nil 通道都会阻塞; 311 | 312 | **答:A B C** 313 | 314 | ## 130. 下面的代码有几处问题?请详细说明。 315 | 316 | ```go 317 | type T struct { 318 | n int 319 | } 320 | 321 | func (t *T) Set(n int) { 322 | t.n = n 323 | } 324 | 325 | func getT() T { 326 | return T{} 327 | } 328 | 329 | func main() { 330 | getT().Set(1) 331 | } 332 | ``` 333 | 334 | **答:有两处问题** 335 | 336 | **解析:** 337 | 338 | - 1.直接返回的 T{} 不可寻址; 339 | - 2.不可寻址的结构体不能调用带结构体指针接收者的方法; 340 | 341 | 修复代码: 342 | 343 | ```go 344 | type T struct { 345 | n int 346 | } 347 | 348 | func (t *T) Set(n int) { 349 | t.n = n 350 | } 351 | 352 | func getT() T { 353 | return T{} 354 | } 355 | 356 | func main() { 357 | t := getT() 358 | t.Set(2) 359 | fmt.Println(t.n) 360 | } 361 | ``` 362 | 363 | ## 131. 下面的代码有什么问题? 364 | 365 | ```go 366 | type N int 367 | 368 | func (n N) value(){ 369 | n++ 370 | fmt.Printf("v:%p,%v\n",&n,n) 371 | } 372 | 373 | func (n *N) pointer(){ 374 | *n++ 375 | fmt.Printf("v:%p,%v\n",n,*n) 376 | } 377 | 378 | 379 | func main() { 380 | 381 | var a N = 25 382 | 383 | p := &a 384 | p1 := &p 385 | 386 | p1.value() 387 | p1.pointer() 388 | } 389 | ``` 390 | 391 | **答:编译错误** 392 | 393 | ```shell 394 | calling method value with receiver p1 (type **N) requires explicit dereference 395 | calling method pointer with receiver p1 (type **N) requires explicit dereference 396 | ``` 397 | 398 | **解析:** 399 | 400 | 不能使用多级指针调用方法。 401 | 402 | ## 132. 下面的代码输出什么? 403 | 404 | ```go 405 | type N int 406 | 407 | func (n N) test(){ 408 | fmt.Println(n) 409 | } 410 | 411 | func main() { 412 | var n N = 10 413 | fmt.Println(n) 414 | 415 | n++ 416 | f1 := N.test 417 | f1(n) 418 | 419 | n++ 420 | f2 := (*N).test 421 | f2(&n) 422 | } 423 | ``` 424 | 425 | **答:10 11 12** 426 | 427 | **解析:** 428 | 429 | 知识点:方法表达式。 430 | 431 | 通过类型引用的方法表达式会被还原成普通函数样式,接收者是第一个参数,调用时显示传参。类型可以是 T 或 *T,只要目标方法存在于该类型的方法集中就可以。 432 | 433 | 还可以直接使用方法表达式调用: 434 | 435 | ```go 436 | func main() { 437 | var n N = 10 438 | 439 | fmt.Println(n) 440 | 441 | n++ 442 | N.test(n) 443 | 444 | n++ 445 | (*N).test(&n) 446 | } 447 | ``` 448 | 449 | ## 133. 关于 channel 下面描述正确的是? 450 | 451 | - A. close() 可以用于只接收通道; 452 | - B. 单向通道可以转换为双向通道; 453 | - C. 不能在单向通道上做逆向操作(例如:只发送通道用于接收); 454 | 455 | **答:C** 456 | 457 | ## 134. 下面的代码有什么问题? 458 | 459 | ```go 460 | type T struct { 461 | n int 462 | } 463 | 464 | func getT() T { 465 | return T{} 466 | } 467 | 468 | func main() { 469 | getT().n = 1 470 | } 471 | ``` 472 | 473 | **答:编译错误** 474 | 475 | ```shell 476 | cannot assign to getT().n 477 | ``` 478 | 479 | **解析:** 480 | 481 | 直接返回的 T{} 无法寻址,不可直接赋值。 482 | 483 | 修复代码: 484 | 485 | ```go 486 | type T struct { 487 | n int 488 | } 489 | 490 | func getT() T { 491 | return T{} 492 | } 493 | 494 | func main() { 495 | t := getT() 496 | p := &t.n // <=> p = &(t.n) 497 | *p = 1 498 | fmt.Println(t.n) 499 | } 500 | ``` 501 | 502 | ## 135. 下面的代码有什么问题? 503 | 504 | ```go 505 | package main 506 | 507 | import "fmt" 508 | 509 | func main() { 510 | s := make([]int, 3, 9) 511 | fmt.Println(len(s)) 512 | s2 := s[4:8] 513 | fmt.Println(len(s2)) 514 | } 515 | ``` 516 | 517 | **答:代码没问题,输出 3 4** 518 | 519 | **解析:** 520 | 521 | **从一个基础切片派生出的子切片的长度可能大于基础切片的长度**。假设基础切片是 baseSlice,使用操作符 [low,high],有如下规则:0 <= low <= high <= cap(baseSlice),只要上述满足这个关系,下标 low 和 high 都可以大于 len(baseSlice)。 522 | 523 | ## 136. 下面代码输出什么? 524 | 525 | ```go 526 | type N int 527 | 528 | func (n N) test(){ 529 | fmt.Println(n) 530 | } 531 | 532 | func main() { 533 | var n N = 10 534 | p := &n 535 | 536 | n++ 537 | f1 := n.test 538 | 539 | n++ 540 | f2 := p.test 541 | 542 | n++ 543 | fmt.Println(n) 544 | 545 | f1() 546 | f2() 547 | } 548 | ``` 549 | 550 | **答:13 11 12** 551 | 552 | **解析:** 553 | 554 | 知识点:方法值。 555 | 556 | 当指针值赋值给变量或者作为函数参数传递时,会立即计算并复制该方法执行所需的接收者对象,与其绑定,以便在稍后执行时,能隐式第传入接收者参数。 557 | 558 | ## 137. 下面哪一行代码会 panic,请说明原因? 559 | 560 | ```go 561 | package main 562 | 563 | func main() { 564 | var x interface{} 565 | var y interface{} = []int{3, 5} 566 | _ = x == x 567 | _ = x == y 568 | _ = y == y 569 | } 570 | ``` 571 | 572 | **答:第 8 行** 573 | 574 | **解析:** 575 | 576 | 因为两个比较值的动态类型为同一个不可比较类型。 577 | 578 | ## 138. 下面的代码输出什么? 579 | 580 | ```go 581 | var o = fmt.Print 582 | 583 | func main() { 584 | c := make(chan int, 1) 585 | for range [3]struct{}{} { 586 | select { 587 | default: 588 | o(1) 589 | case <-c: 590 | o(2) 591 | c = nil 592 | case c <- 1: 593 | o(3) 594 | } 595 | } 596 | } 597 | ``` 598 | 599 | **答:321** 600 | 601 | **解析:** 602 | 603 | 第一次循环,写操作已经准备好,执行 o(3),输出 3; 604 | 605 | 第二次,读操作准备好,执行 o(2),输出 2 并将 c 赋值为 nil; 606 | 607 | 第三次,由于 c 为 nil,走的是 default 分支,输出 1。 608 | 609 | ## 139. 下面的代码输出什么? 610 | 611 | ```go 612 | type T struct { 613 | x int 614 | y *int 615 | } 616 | 617 | func main() { 618 | 619 | i := 20 620 | t := T{10,&i} 621 | 622 | p := &t.x 623 | 624 | *p++ 625 | *p-- 626 | 627 | t.y = p 628 | 629 | fmt.Println(*t.y) 630 | } 631 | ``` 632 | 633 | **答:10** 634 | 635 | **解析:** 636 | 637 | 知识点:运算符优先级。 638 | 639 | 如下规则:递增运算符 ++ 和递减运算符 -- 的优先级低于解引用运算符 * 和取址运算符 &,解引用运算符和取址运算符的优先级低于选择器 . 中的属性选择操作符。 640 | 641 | ## 140. 下面哪一行代码会 panic,请说明原因? 642 | 643 | ```go 644 | package main 645 | 646 | func main() { 647 | x := make([]int, 2, 10) 648 | _ = x[6:10] 649 | _ = x[6:] 650 | _ = x[2:] 651 | } 652 | ``` 653 | 654 | **答:第 6 行** 655 | 656 | **解析:** 657 | 658 | 第 6 行,截取符号 [i:j],如果 j 省略,默认是原切片或者数组的长度,x 的长度是 2,小于起始下标 6 ,所以 panic。 659 | 660 | ## 141. 下面的代码输出什么? 661 | 662 | ```go 663 | type N int 664 | 665 | func (n *N) test(){ 666 | fmt.Println(*n) 667 | } 668 | 669 | func main() { 670 | var n N = 10 671 | p := &n 672 | 673 | n++ 674 | f1 := n.test 675 | 676 | n++ 677 | f2 := p.test 678 | 679 | n++ 680 | fmt.Println(n) 681 | 682 | f1() 683 | f2() 684 | } 685 | ``` 686 | 687 | **答:13 13 13** 688 | 689 | **解析:** 690 | 691 | 知识点:方法值。 692 | 693 | 当目标方法的接收者是指针类型时,那么被复制的就是指针。 694 | 695 | ## 142. 下面哪一行代码会 panic,请说明原因? 696 | 697 | ```go 698 | package main 699 | 700 | func main() { 701 | var m map[int]bool // nil 702 | _ = m[123] 703 | var p *[5]string // nil 704 | for range p { 705 | _ = len(p) 706 | } 707 | var s []int // nil 708 | _ = s[:] 709 | s, s[0] = []int{1, 2}, 9 710 | } 711 | ``` 712 | 713 | **答:第 12 行** 714 | 715 | **解析:** 716 | 717 | 因为左侧的 s[0] 中的 s 为 nil。 718 | 719 | ## 143. 下面哪一行代码会 panic,请说明原因? 720 | 721 | ```go 722 | package main 723 | 724 | type T struct{} 725 | 726 | func (*T) foo() { 727 | } 728 | 729 | func (T) bar() { 730 | } 731 | 732 | type S struct { 733 | *T 734 | } 735 | 736 | func main() { 737 | s := S{} 738 | _ = s.foo 739 | s.foo() 740 | _ = s.bar 741 | } 742 | ``` 743 | 744 | **答:第 19 行** 745 | 746 | **解析:** 747 | 748 | 因为 s.bar 将被展开为 (*s.T).bar,而 s.T 是个空指针,解引用会 panic。 749 | 750 | 可以使用下面代码输出 s: 751 | 752 | ```go 753 | func main() { 754 | s := S{} 755 | fmt.Printf("%#v",s) // 输出:main.S{T:(*main.T)(nil)} 756 | } 757 | ``` 758 | 759 | ## 144. 下面的代码有什么问题? 760 | 761 | ```go 762 | type data struct { 763 | sync.Mutex 764 | } 765 | 766 | func (d data) test(s string) { 767 | d.Lock() 768 | defer d.Unlock() 769 | 770 | for i:=0;i<5 ;i++ { 771 | fmt.Println(s,i) 772 | time.Sleep(time.Second) 773 | } 774 | } 775 | 776 | 777 | func main() { 778 | 779 | var wg sync.WaitGroup 780 | wg.Add(2) 781 | var d data 782 | 783 | go func() { 784 | defer wg.Done() 785 | d.test("read") 786 | }() 787 | 788 | go func() { 789 | defer wg.Done() 790 | d.test("write") 791 | }() 792 | 793 | wg.Wait() 794 | } 795 | ``` 796 | 797 | **答:锁失效** 798 | 799 | **解析:** 800 | 801 | 将 Mutex 作为匿名字段时,相关的方法必须使用指针接收者,否则会导致锁机制失效。 802 | 803 | 修复代码: 804 | 805 | ```go 806 | func (d *data) test(s string) { // 指针接收者 807 | d.Lock() 808 | defer d.Unlock() 809 | 810 | for i:=0;i<5 ;i++ { 811 | fmt.Println(s,i) 812 | time.Sleep(time.Second) 813 | } 814 | } 815 | ``` 816 | 817 | 或者可以通过嵌入 `*Mutex` 来避免复制的问题,但需要初始化。 818 | 819 | ```go 820 | type data struct { 821 | *sync.Mutex // *Mutex 822 | } 823 | 824 | func (d data) test(s string) { // 值方法 825 | d.Lock() 826 | defer d.Unlock() 827 | 828 | for i := 0; i < 5; i++ { 829 | fmt.Println(s, i) 830 | time.Sleep(time.Second) 831 | } 832 | } 833 | 834 | func main() { 835 | 836 | var wg sync.WaitGroup 837 | wg.Add(2) 838 | 839 | d := data{new(sync.Mutex)} // 初始化 840 | 841 | go func() { 842 | defer wg.Done() 843 | d.test("read") 844 | }() 845 | 846 | go func() { 847 | defer wg.Done() 848 | d.test("write") 849 | }() 850 | 851 | wg.Wait() 852 | } 853 | ``` 854 | 855 | ## 145. 下面这段代码输出什么? 856 | 857 | ```go 858 | func main() { 859 | var k = 1 860 | var s = []int{1, 2} 861 | k, s[k] = 0, 3 862 | fmt.Println(s[0] + s[1]) 863 | } 864 | ``` 865 | 866 | **答:4** 867 | 868 | **解析:** 869 | 870 | 知识点:多重赋值。 871 | 872 | 多重赋值分为两个步骤,有先后顺序: 873 | 874 | - 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式; 875 | - 赋值; 876 | 877 | 所以本例,会先计算 s[k],等号右边是两个表达式是常量,所以赋值运算等同于 `k, s[1] = 0, 3`。 878 | 879 | ## 146. 下面代码输出什么? 880 | 881 | ```go 882 | func main() { 883 | var k = 9 884 | for k = range []int{} {} 885 | fmt.Println(k) 886 | 887 | for k = 0; k < 3; k++ { 888 | } 889 | fmt.Println(k) 890 | 891 | 892 | for k = range (*[3]int)(nil) { 893 | } 894 | fmt.Println(k) 895 | } 896 | ``` 897 | 898 | **答:932** 899 | 900 | ## 147. 下面哪一行代码会 panic,请说明。 901 | 902 | ```go 903 | func main() { 904 | nil := 123 905 | fmt.Println(nil) 906 | var _ map[string]int = nil 907 | } 908 | ``` 909 | 910 | **答:第 4 行** 911 | 912 | **解析:** 913 | 914 | 当前作用域中,预定义的 nil 被覆盖,此时 nil 是 int 类型值,不能赋值给 map 类型。 915 | 916 | ## 148. 下面代码输出什么? 917 | 918 | ```go 919 | func main() { 920 | var x int8 = -128 921 | var y = x/-1 922 | fmt.Println(y) 923 | } 924 | ``` 925 | 926 | **答:-128** 927 | 928 | **解析:** 929 | 930 | 溢出 931 | 932 | ## 149. 下面选项正确的是? 933 | 934 | - A. 类型可以声明的函数体内; 935 | - B. Go 语言支持 ++i 或者 --i 操作; 936 | - C. nil 是关键字; 937 | - D. 匿名函数可以直接赋值给一个变量或者直接执行; 938 | 939 | **答:A D** 940 | 941 | ## 150. 下面的代码输出什么? 942 | 943 | ```go 944 | func F(n int) func() int { 945 | return func() int { 946 | n++ 947 | return n 948 | } 949 | } 950 | 951 | func main() { 952 | f := F(5) 953 | defer func() { 954 | fmt.Println(f()) 955 | }() 956 | defer fmt.Println(f()) 957 | i := f() 958 | fmt.Println(i) 959 | } 960 | ``` 961 | 962 | **答:768** 963 | 964 | **解析:** 965 | 966 | 知识点:`匿名函数`、`defer()`。 967 | 968 | defer() 后面的函数如果带参数,会优先计算参数,并将结果存储在栈中,到真正执行 defer() 的时候取出。 -------------------------------------------------------------------------------- /151-180.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [151\. 下面列举的是 recover() 的几种调用方式,哪些是正确的?](#151-%E4%B8%8B%E9%9D%A2%E5%88%97%E4%B8%BE%E7%9A%84%E6%98%AF-recover-%E7%9A%84%E5%87%A0%E7%A7%8D%E8%B0%83%E7%94%A8%E6%96%B9%E5%BC%8F%E5%93%AA%E4%BA%9B%E6%98%AF%E6%AD%A3%E7%A1%AE%E7%9A%84) 4 | > * [152\. 下面代码输出什么,请说明?](#152-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 5 | > * [153\. flag 是 bool 型变量,下面 if 表达式符合编码规范的是?](#153-flag-%E6%98%AF-bool-%E5%9E%8B%E5%8F%98%E9%87%8F%E4%B8%8B%E9%9D%A2-if-%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%AC%A6%E5%90%88%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E7%9A%84%E6%98%AF) 6 | > * [154\. 下面的代码输出什么,请说明?](#154-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 7 | > * [155\. 下面的代码输出什么?](#155-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 8 | > * [156\. 下面的代码输出什么?](#156-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 9 | > * [157\. 下面的代码输出什么?](#157-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 10 | > * [158\. 下面的代码输出什么?](#158-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 11 | > * [159\. 下面代码有什么问题吗?](#159-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E5%90%97) 12 | > * [160\. 下面代码输出什么,请说明。](#160-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 13 | > * [161\. 关于 slice 或 map 操作,下面正确的是?](#161-%E5%85%B3%E4%BA%8E-slice-%E6%88%96-map-%E6%93%8D%E4%BD%9C%E4%B8%8B%E9%9D%A2%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 14 | > * [162\. 下面代码输出什么?](#162-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [163\. 关于字符串连接,下面语法正确的是?](#163-%E5%85%B3%E4%BA%8E%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BF%9E%E6%8E%A5%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 16 | > * [164\. 下面代码能编译通过吗?可以的话,输出什么?](#164-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%83%BD%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%90%97%E5%8F%AF%E4%BB%A5%E7%9A%84%E8%AF%9D%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 17 | > * [165\. 判断题:对变量x的取反操作是 ~x?](#165-%E5%88%A4%E6%96%AD%E9%A2%98%E5%AF%B9%E5%8F%98%E9%87%8Fx%E7%9A%84%E5%8F%96%E5%8F%8D%E6%93%8D%E4%BD%9C%E6%98%AF-x) 18 | > * [166\. 下面代码输出什么,请说明原因。](#166-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 19 | > * [167\. 下面的代码输出什么,请说明。](#167-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 20 | > * [168\. 下面的代码输出什么,请说明?](#168-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 21 | > * [169\. 下面代码输出什么?](#169-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 22 | > * [170\. 下面的代码能编译通过吗?可以的话输出什么,请说明?](#170-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%83%BD%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%90%97%E5%8F%AF%E4%BB%A5%E7%9A%84%E8%AF%9D%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E) 23 | > * [171\. 下面代码有什么问题,请说明?](#171-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E8%AF%B7%E8%AF%B4%E6%98%8E) 24 | > * [172\. 假设 x 已声明,y 未声明,下面 4 行代码哪些是正确的。错误的请说明原因?](#172-%E5%81%87%E8%AE%BE-x-%E5%B7%B2%E5%A3%B0%E6%98%8Ey-%E6%9C%AA%E5%A3%B0%E6%98%8E%E4%B8%8B%E9%9D%A2-4-%E8%A1%8C%E4%BB%A3%E7%A0%81%E5%93%AA%E4%BA%9B%E6%98%AF%E6%AD%A3%E7%A1%AE%E7%9A%84%E9%94%99%E8%AF%AF%E7%9A%84%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 25 | > * [173\. 下面的代码有什么问题,请说明?](#173-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E8%AF%B7%E8%AF%B4%E6%98%8E) 26 | > * [174\. 下面代码输出什么,为什么?](#174-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 27 | > * [175\. 下面这段代码输出什么?](#175-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 28 | > * [176\. 下面的代码有什么问题?](#176-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 29 | > * [177\. 关于 cap 函数适用下面哪些类型?](#177-%E5%85%B3%E4%BA%8E-cap-%E5%87%BD%E6%95%B0%E9%80%82%E7%94%A8%E4%B8%8B%E9%9D%A2%E5%93%AA%E4%BA%9B%E7%B1%BB%E5%9E%8B) 30 | > * [178\. 下面代码输出什么?](#178-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 31 | > * [179\. 关于 switch 语句,下面说法正确的是?](#179-%E5%85%B3%E4%BA%8E-switch-%E8%AF%AD%E5%8F%A5%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 32 | > * [180\. 下面代码能编译通过吗?可以的话,输出什么?](#180-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%83%BD%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%90%97%E5%8F%AF%E4%BB%A5%E7%9A%84%E8%AF%9D%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 33 | > 34 | > * [181\.181-194题](https://github.com/yqchilde/Golang-Interview/blob/master/181-194.md) 35 | 36 | ## 151. 下面列举的是 recover() 的几种调用方式,哪些是正确的? 37 | 38 | * A 39 | 40 | ```go 41 | func main() { 42 | recover() 43 | panic(1) 44 | } 45 | ``` 46 | 47 | * B 48 | 49 | ```go 50 | func main() { 51 | defer recover() 52 | panic(1) 53 | } 54 | ``` 55 | 56 | * C 57 | 58 | ```go 59 | func main() { 60 | defer func() { 61 | recover() 62 | }() 63 | panic(1) 64 | } 65 | ``` 66 | 67 | * D 68 | 69 | ```go 70 | func main() { 71 | defer func() { 72 | defer func() { 73 | recover() 74 | }() 75 | }() 76 | panic(1) 77 | } 78 | ``` 79 | 80 | **答:C** 81 | 82 | **解析:** 83 | 84 | recover() 必须在 defer() 函数中直接调用才有效。上面其他几种情况调用都是无效的:直接调用 recover()、在 defer() 中直接调用 recover() 和 defer() 调用时多层嵌套。 85 | 86 | ## 152. 下面代码输出什么,请说明? 87 | 88 | ```go 89 | func main() { 90 | defer func() { 91 | fmt.Print(recover()) 92 | }() 93 | defer func() { 94 | defer fmt.Print(recover()) 95 | panic(1) 96 | }() 97 | defer recover() 98 | panic(2) 99 | } 100 | ``` 101 | 102 | **答:21** 103 | 104 | **解析:** 105 | 106 | recover() 必须在 defer() 函数中调用才有效,所以第 9 行代码捕获是无效的。在调用 defer() 时,便会计算函数的参数并压入栈中,所以执行第 6 行代码时,此时便会捕获 panic(2);此后的 panic(1),会被上一层的 recover() 捕获。所以输出 21。 107 | 108 | ## 153. flag 是 bool 型变量,下面 if 表达式符合编码规范的是? 109 | 110 | - A. if flag == 1 111 | - B. if flag 112 | - C. if flag == false 113 | - D. if !flag 114 | 115 | **答:B C D** 116 | 117 | ## 154. 下面的代码输出什么,请说明? 118 | 119 | ```go 120 | func main() { 121 | defer func() { 122 | fmt.Print(recover()) 123 | }() 124 | defer func() { 125 | defer func() { 126 | fmt.Print(recover()) 127 | }() 128 | panic(1) 129 | }() 130 | defer recover() 131 | panic(2) 132 | } 133 | ``` 134 | 135 | **答:12** 136 | 137 | **解析:** 138 | 139 | 152题与之类似 140 | 141 | ## 155. 下面的代码输出什么? 142 | 143 | ```go 144 | type T struct { 145 | n int 146 | } 147 | 148 | func main() { 149 | ts := [2]T{} 150 | for i, t := range ts { 151 | switch i { 152 | case 0: 153 | t.n = 3 154 | ts[1].n = 9 155 | case 1: 156 | fmt.Print(t.n, " ") 157 | } 158 | } 159 | fmt.Print(ts) 160 | } 161 | ``` 162 | 163 | **答:0 [{0} {9}]** 164 | 165 | **解析:** 166 | 167 | 知识点:for-range 循环数组。 168 | 169 | 此时使用的是数组 ts 的副本,所以 t.n = 3 的赋值操作不会影响原数组。 170 | 171 | ## 156. 下面的代码输出什么? 172 | 173 | ```go 174 | type T struct { 175 | n int 176 | } 177 | 178 | func main() { 179 | ts := [2]T{} 180 | for i, t := range &ts { 181 | switch i { 182 | case 0: 183 | t.n = 3 184 | ts[1].n = 9 185 | case 1: 186 | fmt.Print(t.n, " ") 187 | } 188 | } 189 | fmt.Print(ts) 190 | } 191 | ``` 192 | 193 | **答:9 [{0} {9}]** 194 | 195 | **解析:** 196 | 197 | 知识点:for-range 数组指针。 198 | 199 | for-range 循环中的循环变量 t 是原数组元素的副本。如果数组元素是结构体值,则副本的字段和原数组字段是两个不同的值。 200 | 201 | ## 157. 下面的代码输出什么? 202 | 203 | ```go 204 | type T struct { 205 | n int 206 | } 207 | 208 | func main() { 209 | ts := [2]T{} 210 | for i := range ts[:] { 211 | switch i { 212 | case 0: 213 | ts[1].n = 9 214 | case 1: 215 | fmt.Print(ts[i].n, " ") 216 | } 217 | } 218 | fmt.Print(ts) 219 | } 220 | ``` 221 | 222 | **答:9 [{0} {9}]** 223 | 224 | **解析:** 225 | 226 | 知识点:for-range 切片。 227 | 228 | for-range 切片时使用的是切片的副本,但不会复制底层数组,换句话说,此副本切片与原数组共享底层数组。 229 | 230 | ## 158. 下面的代码输出什么? 231 | 232 | ```go 233 | type T struct { 234 | n int 235 | } 236 | 237 | func main() { 238 | ts := [2]T{} 239 | for i := range ts[:] { 240 | switch t := &ts[i]; i { 241 | case 0: 242 | t.n = 3; 243 | ts[1].n = 9 244 | case 1: 245 | fmt.Print(t.n, " ") 246 | } 247 | } 248 | fmt.Print(ts) 249 | } 250 | ``` 251 | 252 | **答:9 [{3} {9}]** 253 | 254 | **解析:** 255 | 256 | 知识点:for-range 切片。参考前几道题的解析,这道题的答案应该很明显。 257 | 258 | ## 159. 下面代码有什么问题吗? 259 | 260 | ```go 261 | func main() { 262 | 263 | for i:=0;i<10 ;i++ { 264 | loop: 265 | println(i) 266 | } 267 | goto loop 268 | } 269 | ``` 270 | 271 | **解析:** 272 | 273 | goto 不能跳转到其他函数或者内层代码。编译报错: 274 | 275 | ```shell 276 | goto loop jumps into block starting at 277 | ``` 278 | 279 | ## 160. 下面代码输出什么,请说明。 280 | 281 | ```go 282 | func main() { 283 | x := []int{0, 1, 2} 284 | y := [3]*int{} 285 | for i, v := range x { 286 | defer func() { 287 | print(v) 288 | }() 289 | y[i] = &v 290 | } 291 | print(*y[0], *y[1], *y[2]) 292 | } 293 | ``` 294 | 295 | **答:22222** 296 | 297 | **解析:** 298 | 299 | 知识点:defer()、for-range。 300 | 301 | for-range 虽然使用的是 :=,但是 v 不会重新声明,可以打印 v 的地址验证下。 302 | 303 | ## 161. 关于 slice 或 map 操作,下面正确的是? 304 | 305 | * A 306 | 307 | ```go 308 | var s []int 309 | s = append(s,1) 310 | ``` 311 | 312 | * B 313 | 314 | ```go 315 | var m map[string]int 316 | m["one"] = 1 317 | ``` 318 | 319 | * C 320 | 321 | ```go 322 | var s []int 323 | s = make([]int, 0) 324 | s = append(s,1) 325 | ``` 326 | 327 | * D 328 | 329 | ```go 330 | var m map[string]int 331 | m = make(map[string]int) 332 | m["one"] = 1 333 | ``` 334 | 335 | **答:A C D** 336 | 337 | ## 162. 下面代码输出什么? 338 | 339 | ```go 340 | func test(x int) (func(), func()) { 341 | return func() { 342 | println(x) 343 | x += 10 344 | }, func() { 345 | println(x) 346 | } 347 | } 348 | 349 | func main() { 350 | a, b := test(100) 351 | a() 352 | b() 353 | } 354 | ``` 355 | 356 | **答:100 110** 357 | 358 | **解析:** 359 | 360 | 知识点:闭包引用相同变量。 361 | 362 | ## 163. 关于字符串连接,下面语法正确的是? 363 | 364 | - A. str := 'abc' + '123' 365 | - B. str := "abc" + "123" 366 | - C. str := '123' + "abc" 367 | - D. fmt.Sprintf("abc%d", 123) 368 | 369 | **答:B D** 370 | 371 | **解析:** 372 | 373 | 知识点:单引号、双引号和字符串连接。 374 | 375 | 在 Go 语言中,双引号用来表示字符串 string,其实质是一个 byte 类型的数组,单引号表示 rune 类型。 376 | 377 | ## 164. 下面代码能编译通过吗?可以的话,输出什么? 378 | 379 | ```go 380 | func main() { 381 | 382 | println(DeferTest1(1)) 383 | println(DeferTest2(1)) 384 | } 385 | 386 | func DeferTest1(i int) (r int) { 387 | r = i 388 | defer func() { 389 | r += 3 390 | }() 391 | return r 392 | } 393 | 394 | func DeferTest2(i int) (r int) { 395 | defer func() { 396 | r += i 397 | }() 398 | return 2 399 | } 400 | ``` 401 | 402 | **答:43** 403 | 404 | ## 165. 判断题:对变量x的取反操作是 ~x? 405 | 406 | **答:错** 407 | 408 | **解析:** 409 | 410 | Go 语言的取反操作是 `^`,它返回一个每个 bit 位都取反的数。作用类似在 C、C#、Java 语言中中符号 ~,对于有符号的整数来说,是按照补码进行取反操作的(快速计算方法:对数 a 取反,结果为 -(a+1) ),对于无符号整数来说就是按位取反。 411 | 412 | ## 166. 下面代码输出什么,请说明原因。 413 | 414 | ```go 415 | type Slice []int 416 | 417 | func NewSlice() Slice { 418 | return make(Slice, 0) 419 | } 420 | func (s *Slice) Add(elem int) *Slice { 421 | *s = append(*s, elem) 422 | fmt.Print(elem) 423 | return s 424 | } 425 | func main() { 426 | s := NewSlice() 427 | defer s.Add(1).Add(2) 428 | s.Add(3) 429 | } 430 | ``` 431 | 432 | **答:132** 433 | 434 | **解析:** 435 | 436 | 这一题有两点需要注意: 437 | 438 | 1. Add() 方法的返回值依然是指针类型 *Slice,所以可以循环调用方法 Add(); 439 | 2. defer 函数的参数(包括接收者)是在 defer 语句出现的位置做计算的,而不是在函数执行的时候计算的,所以 s.Add(1) 会先于 s.Add(3) 执行。 440 | 441 | ## 167. 下面的代码输出什么,请说明。 442 | 443 | ```go 444 | type Slice []int 445 | 446 | func NewSlice() Slice { 447 | return make(Slice, 0) 448 | } 449 | func (s *Slice) Add(elem int) *Slice { 450 | *s = append(*s, elem) 451 | fmt.Print(elem) 452 | return s 453 | } 454 | func main() { 455 | s := NewSlice() 456 | defer func() { 457 | s.Add(1).Add(2) 458 | }() 459 | s.Add(3) 460 | } 461 | ``` 462 | 463 | **答:312** 464 | 465 | **解析:** 466 | 467 | 对比昨天的第`166`题,本题的 s.Add(1).Add(2) 作为一个整体包在一个匿名函数中,会延迟执行。 468 | 469 | ## 168. 下面的代码输出什么,请说明? 470 | 471 | ```go 472 | type Orange struct { 473 | Quantity int 474 | } 475 | 476 | func (o *Orange) Increase(n int) { 477 | o.Quantity += n 478 | } 479 | 480 | func (o *Orange) Decrease(n int) { 481 | o.Quantity -= n 482 | } 483 | 484 | func (o *Orange) String() string { 485 | return fmt.Sprintf("%#v", o.Quantity) 486 | } 487 | 488 | func main() { 489 | var orange Orange 490 | orange.Increase(10) 491 | orange.Decrease(5) 492 | fmt.Println(orange) 493 | } 494 | ``` 495 | 496 | **答:{5}** 497 | 498 | **解析:** 499 | 500 | 这道题容易忽视的点是,String() 是指针方法,而不是值方法,所以使用 Println() 输出时不会调用到 String() 方法。 501 | 502 | 可以这样修复: 503 | 504 | ```go 505 | func main() { 506 | orange := &Orange{} 507 | orange.Increase(10) 508 | orange.Decrease(5) 509 | fmt.Println(orange) 510 | } 511 | ``` 512 | 513 | ## 169. 下面代码输出什么? 514 | 515 | ```go 516 | func test() []func() { 517 | var funs []func() 518 | for i := 0; i < 2; i++ { 519 | funs = append(funs, func() { 520 | println(&i, i) 521 | }) 522 | } 523 | return funs 524 | } 525 | 526 | func main() { 527 | funs := test() 528 | for _, f := range funs { 529 | f() 530 | } 531 | } 532 | ``` 533 | 534 | **答:** 535 | 536 | ```shell 537 | 10xc000018058 2 538 | 20xc000018058 2 539 | ``` 540 | 541 | **解析:** 542 | 543 | 知识点:闭包延迟求值。for 循环局部变量 i,匿名函数每一次使用的都是同一个变量。(说明:i 的地址,输出可能与上面的不一样)。 544 | 545 | ## 170. 下面的代码能编译通过吗?可以的话输出什么,请说明? 546 | 547 | ```go 548 | var f = func(i int) { 549 | print("x") 550 | } 551 | 552 | func main() { 553 | f := func(i int) { 554 | print(i) 555 | if i > 0 { 556 | f(i - 1) 557 | } 558 | } 559 | f(10) 560 | } 561 | ``` 562 | 563 | **答:10x** 564 | 565 | **解析:** 566 | 567 | 这道题一眼看上去会输出 109876543210,其实这是错误的答案,这里不是递归。假设 main() 函数里为 f2(),外面的为 f1(),当声明 f2() 时,调用的是已经完成声明的 f1()。 568 | 569 | 看下面这段代码你应该会更容易理解一点: 570 | 571 | ```go 572 | var x = 23 573 | 574 | func main() { 575 | x := 2*x - 4 576 | println(x) // 输出:42 577 | } 578 | ``` 579 | 580 | ## 171. 下面代码有什么问题,请说明? 581 | 582 | ```go 583 | func main() { 584 | runtime.GOMAXPROCS(1) 585 | 586 | go func() { 587 | for i:=0;i<10 ;i++ { 588 | fmt.Println(i) 589 | } 590 | }() 591 | 592 | for {} 593 | } 594 | ``` 595 | 596 | **答:** [讨论 #11](https://github.com/yqchilde/Golang-Interview/issues/11) 597 | 598 | 以上代码在go1.14版本之前(不含1.14版本): for {} 独占 CPU 资源导致其他 Goroutine 饿死, 599 | 600 | 在go1.14版本之后(包含go1.14): 会打印0123456789, 并且主程会进入死循环 601 | 602 | **解析:** 603 | 604 | 可以通过阻塞的方式避免 CPU 占用,修复代码: 605 | 606 | ```go 607 | func main() { 608 | runtime.GOMAXPROCS(1) 609 | 610 | go func() { 611 | for i:=0;i<10 ;i++ { 612 | fmt.Println(i) 613 | } 614 | os.Exit(0) 615 | }() 616 | 617 | select {} 618 | } 619 | ``` 620 | 621 | ## 172. 假设 x 已声明,y 未声明,下面 4 行代码哪些是正确的。错误的请说明原因? 622 | 623 | ```go 624 | x, _ := f() // 1 625 | x, _ = f() // 2 626 | x, y := f() // 3 627 | x, y = f() // 4 628 | ``` 629 | 630 | **答:2、3正确** 631 | 632 | **解析:** 633 | 634 | 知识点:简短变量声明。使用简短变量声明有几个需要注意的地方: 635 | 636 | - 只能用于函数内部; 637 | - 短变量声明语句中至少要声明一个新的变量; 638 | 639 | ## 173. 下面的代码有什么问题,请说明? 640 | 641 | ```go 642 | func main() { 643 | f, err := os.Open("file") 644 | defer f.Close() 645 | if err != nil { 646 | return 647 | } 648 | 649 | b, err := ioutil.ReadAll(f) 650 | println(string(b)) 651 | } 652 | ``` 653 | 654 | **答:defer 语句应该放在 if() 语句后面,先判断 err,再 defer 关闭文件句柄。** 655 | 656 | **解析:** 657 | 658 | 修复代码: 659 | 660 | ```go 661 | func main() { 662 | f, err := os.Open("file") 663 | if err != nil { 664 | return 665 | } 666 | defer f.Close() 667 | 668 | b, err := ioutil.ReadAll(f) 669 | println(string(b)) 670 | } 671 | ``` 672 | 673 | ## 174. 下面代码输出什么,为什么? 674 | 675 | ```go 676 | func f() { 677 | defer func() { 678 | if r := recover(); r != nil { 679 | fmt.Printf("recover:%#v", r) 680 | } 681 | }() 682 | panic(1) 683 | panic(2) 684 | } 685 | 686 | func main() { 687 | f() 688 | } 689 | ``` 690 | 691 | **答:recover:1** 692 | 693 | **解析:** 694 | 695 | 知识点:`panic`、`recover()`。 696 | 697 | 当程序 panic 时就不会往下执行,可以使用 recover() 捕获 panic 的内容。 698 | 699 | ## 175. 下面这段代码输出什么? 700 | 701 | ```go 702 | type S1 struct{} 703 | 704 | func (s1 S1) f() { 705 | fmt.Println("S1.f()") 706 | } 707 | func (s1 S1) g() { 708 | fmt.Println("S1.g()") 709 | } 710 | 711 | type S2 struct { 712 | S1 713 | } 714 | 715 | func (s2 S2) f() { 716 | fmt.Println("S2.f()") 717 | } 718 | 719 | type I interface { 720 | f() 721 | } 722 | 723 | func printType(i I) { 724 | 725 | fmt.Printf("%T\n", i) 726 | if s1, ok := i.(S1); ok { 727 | s1.f() 728 | s1.g() 729 | } 730 | if s2, ok := i.(S2); ok { 731 | s2.f() 732 | s2.g() 733 | } 734 | } 735 | 736 | func main() { 737 | printType(S1{}) 738 | printType(S2{}) 739 | } 740 | ``` 741 | 742 | **答:** 743 | 744 | ```shell 745 | main.S1 746 | S1.f() 747 | S1.g() 748 | main.S2 749 | S2.f() 750 | S1.g() 751 | ``` 752 | 753 | **解析:** 754 | 755 | 知识点:类型断言,结构体嵌套。 756 | 757 | 结构体 S2 嵌套了结构体 S1,S2 自己没有实现 g() ,调用的是 S1 的 g()。 758 | 759 | ## 176. 下面的代码有什么问题? 760 | 761 | ```go 762 | func main() { 763 | var wg sync.WaitGroup 764 | wg.Add(1) 765 | go func() { 766 | fmt.Println("1") 767 | wg.Done() 768 | wg.Add(1) 769 | }() 770 | wg.Wait() 771 | } 772 | ``` 773 | 774 | **解析:** 775 | 776 | 协程里面,使用 wg.Add(1) 但是没有 wg.Done(),导致 panic()。 777 | 778 | ## 177. 关于 cap 函数适用下面哪些类型? 779 | 780 | - A. 数组; 781 | - B. channel; 782 | - C. map; 783 | - D. slice; 784 | 785 | **答:A B D** 786 | 787 | **解析:** 788 | 789 | `cap()`函数的作用是: 790 | 791 | * array 返回数组的元素个数 792 | * slice 返回slice的最大容量 793 | * channel 返回 channel的容量 794 | 795 | ## 178. 下面代码输出什么? 796 | 797 | ```go 798 | func hello(num ...int) { 799 | num[0] = 18 800 | } 801 | 802 | func Test13(t *testing.T) { 803 | i := []int{5, 6, 7} 804 | hello(i...) 805 | fmt.Println(i[0]) 806 | } 807 | 808 | func main() { 809 | t := &testing.T{} 810 | Test13(t) 811 | } 812 | ``` 813 | 814 | - A. 18 815 | - B. 5 816 | - C. Compilation error 817 | 818 | **答:A** 819 | 820 | **解析:** 821 | 822 | 可变函数是指针传递 823 | 824 | ## 179. 关于 switch 语句,下面说法正确的是? 825 | 826 | - A. 单个 case 中,可以出现多个结果选项; 827 | - B. 需要使用 break 来明确退出一个 case; 828 | - C. 只有在 case 中明确添加 fallthrought 关键字,才会继续执行紧跟的下一个 case; 829 | - D. 条件表达式必须为常量或者整数; 830 | 831 | **答:A C** 832 | 833 | ## 180. 下面代码能编译通过吗?可以的话,输出什么? 834 | 835 | ```go 836 | func alwaysFalse() bool { 837 | return false 838 | } 839 | 840 | func main() { 841 | switch alwaysFalse() 842 | { 843 | case true: 844 | println(true) 845 | case false: 846 | println(false) 847 | } 848 | } 849 | ``` 850 | 851 | **答:可以编译通过,输出:true** 852 | 853 | **解析:** 854 | 855 | Go 代码断行规则。 -------------------------------------------------------------------------------- /181-194.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [181\. interface\{\} 是可以指向任意对象的 Any 类型,是否正确?](#181-interface-%E6%98%AF%E5%8F%AF%E4%BB%A5%E6%8C%87%E5%90%91%E4%BB%BB%E6%84%8F%E5%AF%B9%E8%B1%A1%E7%9A%84-any-%E7%B1%BB%E5%9E%8B%E6%98%AF%E5%90%A6%E6%AD%A3%E7%A1%AE) 4 | > * [182\. 下面的代码有什么问题?](#182-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 5 | > * [183\. 定义一个包内全局字符串变量,下面语法正确的是?](#183-%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E5%8C%85%E5%86%85%E5%85%A8%E5%B1%80%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%98%E9%87%8F%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 6 | > * [184\. 下面的代码有什么问题?](#184-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 7 | > * [185\. 下面的代码输出什么?](#185-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 8 | > * [186\. 下面代码中的指针 p 为野指针,因为返回的栈内存在函数结束时会被释放?](#186-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E4%B8%AD%E7%9A%84%E6%8C%87%E9%92%88-p-%E4%B8%BA%E9%87%8E%E6%8C%87%E9%92%88%E5%9B%A0%E4%B8%BA%E8%BF%94%E5%9B%9E%E7%9A%84%E6%A0%88%E5%86%85%E5%AD%98%E5%9C%A8%E5%87%BD%E6%95%B0%E7%BB%93%E6%9D%9F%E6%97%B6%E4%BC%9A%E8%A2%AB%E9%87%8A%E6%94%BE) 9 | > * [187\. 下面这段代码输出什么?](#187-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 10 | > * [188\. 下面代码输出什么?](#188-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 11 | > * [189\. 同级文件的包名不允许有多个,是否正确?](#189-%E5%90%8C%E7%BA%A7%E6%96%87%E4%BB%B6%E7%9A%84%E5%8C%85%E5%90%8D%E4%B8%8D%E5%85%81%E8%AE%B8%E6%9C%89%E5%A4%9A%E4%B8%AA%E6%98%AF%E5%90%A6%E6%AD%A3%E7%A1%AE) 12 | > * [190\. 下面的代码有什么问题,请说明。](#190-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E8%AF%B7%E8%AF%B4%E6%98%8E) 13 | > * [191\. 函数执行时,如果由于 panic 导致了异常,则延迟函数不会执行。这一说法是否正确?](#191-%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E6%97%B6%E5%A6%82%E6%9E%9C%E7%94%B1%E4%BA%8E-panic-%E5%AF%BC%E8%87%B4%E4%BA%86%E5%BC%82%E5%B8%B8%E5%88%99%E5%BB%B6%E8%BF%9F%E5%87%BD%E6%95%B0%E4%B8%8D%E4%BC%9A%E6%89%A7%E8%A1%8C%E8%BF%99%E4%B8%80%E8%AF%B4%E6%B3%95%E6%98%AF%E5%90%A6%E6%AD%A3%E7%A1%AE) 14 | > * [192\. 下面代码输出什么?](#192-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [193\. 下面这段代码输出什么?请简要说明。](#193-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E7%AE%80%E8%A6%81%E8%AF%B4%E6%98%8E) 16 | > * [194\. 下面代码输出什么?](#194-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 17 | 18 | ## 181. interface{} 是可以指向任意对象的 Any 类型,是否正确? 19 | 20 | - A. false 21 | - B. true 22 | 23 | **答:B** 24 | 25 | ## 182. 下面的代码有什么问题? 26 | 27 | ```go 28 | type ConfigOne struct { 29 | Daemon string 30 | } 31 | 32 | func (c *ConfigOne) String() string { 33 | return fmt.Sprintf("print: %v", c) 34 | } 35 | 36 | func main() { 37 | c := &ConfigOne{} 38 | c.String() 39 | } 40 | ``` 41 | 42 | **答:无限递归循环,栈溢出。** 43 | 44 | **解析:** 45 | 46 | 知识点:类型的 String() 方法。如果类型定义了 String() 方法,使用 Printf()、Print() 、 Println() 、 Sprintf() 等格式化输出时会自动使用 String() 方法。 47 | 48 | ## 183. 定义一个包内全局字符串变量,下面语法正确的是? 49 | 50 | - A. var str string 51 | - B. str := "" 52 | - C. str = "" 53 | - D. var str = "" 54 | 55 | **答:A D** 56 | 57 | **解析:** 58 | 59 | 全局变量要定义在函数之外,而在函数之外定义的变量只能用 var 定义。短变量声明 := 只能用于函数之内。 60 | 61 | ## 184. 下面的代码有什么问题? 62 | 63 | ```go 64 | func main() { 65 | 66 | wg := sync.WaitGroup{} 67 | 68 | for i := 0; i < 5; i++ { 69 | go func(wg sync.WaitGroup, i int) { 70 | wg.Add(1) 71 | fmt.Printf("i:%d\n", i) 72 | wg.Done() 73 | }(wg, i) 74 | } 75 | 76 | wg.Wait() 77 | 78 | fmt.Println("exit") 79 | } 80 | ``` 81 | 82 | **解析:** 83 | 84 | 知识点:WaitGroup 的使用。存在两个问题: 85 | 86 | - 在协程中使用 wg.Add(); 87 | - 使用了 sync.WaitGroup 副本; 88 | 89 | 修复代码: 90 | 91 | ```go 92 | func main() { 93 | 94 | wg := sync.WaitGroup{} 95 | 96 | for i := 0; i < 5; i++ { 97 | wg.Add(1) 98 | go func(i int) { 99 | fmt.Printf("i:%d\n", i) 100 | wg.Done() 101 | }(i) 102 | } 103 | 104 | wg.Wait() 105 | 106 | fmt.Println("exit") 107 | } 108 | ``` 109 | 110 | 或者: 111 | 112 | ```go 113 | func main() { 114 | 115 | wg := &sync.WaitGroup{} 116 | 117 | for i := 0; i < 5; i++ { 118 | wg.Add(1) 119 | go func(wg *sync.WaitGroup,i int) { 120 | fmt.Printf("i:%d\n", i) 121 | wg.Done() 122 | }(wg,i) 123 | } 124 | 125 | wg.Wait() 126 | 127 | fmt.Println("exit") 128 | } 129 | ``` 130 | 131 | ## 185. 下面的代码输出什么? 132 | 133 | ```go 134 | func main() { 135 | var a []int = nil 136 | a, a[0] = []int{1, 2}, 9 137 | fmt.Println(a) 138 | } 139 | ``` 140 | 141 | **答:运行时错误** 142 | 143 | **解析:** 144 | 145 | 知识点:多重赋值。 146 | 147 | 多重赋值分为两个步骤,有先后顺序: 148 | 149 | - 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式; 150 | - 赋值; 151 | 152 | ## 186. 下面代码中的指针 p 为野指针,因为返回的栈内存在函数结束时会被释放? 153 | 154 | ```go 155 | type TimesMatcher struct { 156 | base int 157 | } 158 | 159 | func NewTimesMatcher(base int) *TimesMatcher { 160 | return &TimesMatcher{base:base} 161 | } 162 | 163 | func main() { 164 | p := NewTimesMatcher(3) 165 | fmt.Println(p) 166 | } 167 | ``` 168 | 169 | - A. false 170 | - B. true 171 | 172 | **答:A** 173 | 174 | **解析:** 175 | 176 | Go语言的内存回收机制规定,只要有一个指针指向引用一个变量,那么这个变量就不会被释放(内存逃逸),因此在 Go 语言中返回函数参数或临时变量是安全的。 177 | 178 | ## 187. 下面这段代码输出什么? 179 | 180 | ```go 181 | func main() { 182 | count := 0 183 | for i := range [256]struct{}{} { 184 | m, n := byte(i), int8(i) 185 | if n == -n { 186 | count++ 187 | } 188 | if m == -m { 189 | count++ 190 | } 191 | } 192 | fmt.Println(count) 193 | } 194 | ``` 195 | 196 | **解析:** 197 | 198 | [讨论 #8](https://github.com/yqchilde/Golang-Interview/issues/8) 199 | 200 | 知识点:数值溢出。当 i 的值为 0、128 是会发生相等情况,注意 byte 是 uint8 的别名。 201 | 202 | ## 188. 下面代码输出什么? 203 | 204 | ```go 205 | const ( 206 | azero = iota 207 | aone = iota 208 | ) 209 | 210 | const ( 211 | info = "msg" 212 | bzero = iota 213 | bone = iota 214 | ) 215 | 216 | func main() { 217 | fmt.Println(azero, aone) 218 | fmt.Println(bzero, bone) 219 | } 220 | ``` 221 | 222 | **答:0 1 1 2** 223 | 224 | **解析:** 225 | 226 | 知识点:iota 的使用。这道题易错点在 bzero、bone 的值,在一个常量声明代码块中,如果 iota 没出现在第一行,则常量的初始值就是非 0 值。 227 | 228 | ## 189. 同级文件的包名不允许有多个,是否正确? 229 | 230 | - A. true 231 | - B. false 232 | 233 | **答:A** 234 | 235 | **解析:** 236 | 237 | 一个文件夹下只能有一个包,可以多个.go文件,但这些文件必须属于同一个包。 238 | 239 | ## 190. 下面的代码有什么问题,请说明。 240 | 241 | ```go 242 | type data struct { 243 | name string 244 | } 245 | 246 | func (p *data) print() { 247 | fmt.Println("name:", p.name) 248 | } 249 | 250 | type printer interface { 251 | print() 252 | } 253 | 254 | func main() { 255 | d1 := data{"one"} 256 | d1.print() 257 | 258 | var in printer = data{"two"} 259 | in.print() 260 | } 261 | ``` 262 | 263 | **答:编译报错** 264 | 265 | ```shell 266 | cannot use data literal (type data) as type printer in assignment: 267 | data does not implement printer (print method has pointer receiver) 268 | ``` 269 | 270 | **解析:** 271 | 272 | 结构体类型 data 没有实现接口 printer。知识点:接口。 273 | 274 | ## 191. 函数执行时,如果由于 panic 导致了异常,则延迟函数不会执行。这一说法是否正确? 275 | 276 | - A. true 277 | - B. false 278 | 279 | **答:B** 280 | 281 | **解析:** 282 | 283 | 由 panic 引发异常以后,程序停止执行,然后调用延迟函数(defer),就像程序正常退出一样。 284 | 285 | ## 192. 下面代码输出什么? 286 | 287 | ```go 288 | func main() { 289 | a := [3]int{0, 1, 2} 290 | s := a[1:2] 291 | 292 | s[0] = 11 293 | s = append(s, 12) 294 | s = append(s, 13) 295 | s[0] = 21 296 | 297 | fmt.Println(a) 298 | fmt.Println(s) 299 | } 300 | ``` 301 | 302 | **答:** 303 | 304 | ```go 305 | [0 11 12] 306 | [21 12 13] 307 | ``` 308 | 309 | ## 193. 下面这段代码输出什么?请简要说明。 310 | 311 | ```go 312 | func main() { 313 | fmt.Println(strings.TrimRight("ABBA", "BA")) 314 | } 315 | ``` 316 | 317 | **答:输出结果为""** 318 | 319 | **解析:** 320 | 321 | [题解 #4](https://github.com/yqchilde/Golang-Interview/issues/4) 322 | 323 | strings.TrimRight的作用是把有包含第二个参数的组合项的对应字母都替换掉,比如"BA"的组合集合为{"BA", "AB", "A", "B"}; 324 | 但是它有一个中止条件,如果从右到左有一个字母或字母组合不为"BA"的排列组合集合中的元素,便会停止cut,把当前已cut完的字符串返回 325 | 326 | ## 194. 下面代码输出什么? 327 | 328 | ```go 329 | func main() { 330 | var src, dst []int 331 | src = []int{1, 2, 3} 332 | copy(dst, src) 333 | fmt.Println(dst) 334 | } 335 | ``` 336 | 337 | **答:输出结果为[]** 338 | 339 | **解析:** 340 | 341 | [题解 #5](https://github.com/yqchilde/Golang-Interview/issues/5) 342 | 343 | copy函数实际上会返回一个int值,这个int是一个size,计算逻辑为size = min(len(dst), len(src)),这个size的大小, 344 | 决定了src要copy几个元素给dst,由于题目中,dst声明了,但是没有进行初始化,所以dst的len是0,因此实际没有从src上copy到任何元素给dst 345 | -------------------------------------------------------------------------------- /31-60.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [31\. 定义一个包内全局字符串变量,下面语法正确的是()](#31-%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E5%8C%85%E5%86%85%E5%85%A8%E5%B1%80%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%98%E9%87%8F%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 4 | > * [32\. 下面这段代码输出什么?](#32-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 5 | > * [33\. 下面这段代码输出什么?](#33-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 6 | > * [34\. 下面代码输出什么?](#34-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 7 | > * [35\. 下面代码输出什么?](#35-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 8 | > * [36\. 对 add() 函数调用正确的是()](#36-%E5%AF%B9-add-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 9 | > * [37\. 下面代码下划线处可以填入哪个选项?](#37-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E4%B8%8B%E5%88%92%E7%BA%BF%E5%A4%84%E5%8F%AF%E4%BB%A5%E5%A1%AB%E5%85%A5%E5%93%AA%E4%B8%AA%E9%80%89%E9%A1%B9) 10 | > * [38\. 下面这段代码输出什么?](#38-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 11 | > * [39\. 下面这段代码输出什么?](#39-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 12 | > * [40\. 切片 a、b、c 的长度和容量分别是多少?](#40-%E5%88%87%E7%89%87-abc-%E7%9A%84%E9%95%BF%E5%BA%A6%E5%92%8C%E5%AE%B9%E9%87%8F%E5%88%86%E5%88%AB%E6%98%AF%E5%A4%9A%E5%B0%91) 13 | > * [41\. 下面代码中 A B 两处应该怎么修改才能顺利编译?](#41-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E4%B8%AD-a-b-%E4%B8%A4%E5%A4%84%E5%BA%94%E8%AF%A5%E6%80%8E%E4%B9%88%E4%BF%AE%E6%94%B9%E6%89%8D%E8%83%BD%E9%A1%BA%E5%88%A9%E7%BC%96%E8%AF%91) 14 | > * [42\. 下面代码输出什么?](#42-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [43\. 下面代码中,x 已声明,y 没有声明,判断每条语句的对错。](#43-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E4%B8%ADx-%E5%B7%B2%E5%A3%B0%E6%98%8Ey-%E6%B2%A1%E6%9C%89%E5%A3%B0%E6%98%8E%E5%88%A4%E6%96%AD%E6%AF%8F%E6%9D%A1%E8%AF%AD%E5%8F%A5%E7%9A%84%E5%AF%B9%E9%94%99) 16 | > * [44\. 下面代码输出什么?](#44-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 17 | > * [45\. 下面代码输出什么?](#45-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 18 | > * [46\. f1()、f2()、f3() 函数分别返回什么?](#46-f1f2f3-%E5%87%BD%E6%95%B0%E5%88%86%E5%88%AB%E8%BF%94%E5%9B%9E%E4%BB%80%E4%B9%88) 19 | > * [47\. 下面代码段输出什么?](#47-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%AE%B5%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 20 | > * [48\. 下面这段代码正确的输出是什么?](#48-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E6%AD%A3%E7%A1%AE%E7%9A%84%E8%BE%93%E5%87%BA%E6%98%AF%E4%BB%80%E4%B9%88) 21 | > * [49\. 下面代码输出什么?](#49-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 22 | > * [50\. 下面的两个切片声明中有什么区别?哪个更可取?](#50-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%B8%A4%E4%B8%AA%E5%88%87%E7%89%87%E5%A3%B0%E6%98%8E%E4%B8%AD%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB%E5%93%AA%E4%B8%AA%E6%9B%B4%E5%8F%AF%E5%8F%96) 23 | > * [51\. A、B、C、D 哪些选项有语法错误?](#51-abcd-%E5%93%AA%E4%BA%9B%E9%80%89%E9%A1%B9%E6%9C%89%E8%AF%AD%E6%B3%95%E9%94%99%E8%AF%AF) 24 | > * [52\. 下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果?](#52-%E4%B8%8B%E9%9D%A2-ab-%E4%B8%A4%E5%A4%84%E5%BA%94%E8%AF%A5%E5%A1%AB%E5%85%A5%E4%BB%80%E4%B9%88%E4%BB%A3%E7%A0%81%E6%89%8D%E8%83%BD%E7%A1%AE%E4%BF%9D%E9%A1%BA%E5%88%A9%E6%89%93%E5%8D%B0%E5%87%BA%E7%BB%93%E6%9E%9C) 25 | > * [53\. 下面的代码有几处语法问题,各是什么?](#53-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E5%87%A0%E5%A4%84%E8%AF%AD%E6%B3%95%E9%97%AE%E9%A2%98%E5%90%84%E6%98%AF%E4%BB%80%E4%B9%88) 26 | > * [54\. return 之后的 defer 语句会执行吗,下面这段代码输出什么?](#54-return-%E4%B9%8B%E5%90%8E%E7%9A%84-defer-%E8%AF%AD%E5%8F%A5%E4%BC%9A%E6%89%A7%E8%A1%8C%E5%90%97%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 27 | > * [55\. 下面这段代码输出什么?为什么?](#55-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 28 | > * [56\. 下面选项正确的是?](#56-%E4%B8%8B%E9%9D%A2%E9%80%89%E9%A1%B9%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 29 | > * [57\. 下面这段代码输出什么?](#57-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 30 | > * [58\. 下面这段代码输出什么?](#58-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 31 | > * [59\. 下面这段代码输出什么?为什么?](#59-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 32 | > * [60\. 下面这段代码输出什么?为什么?](#60-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 33 | > * [61\. 61-90题](https://github.com/yqchilde/Golang-Interview/blob/master/61-90.md) 34 | 35 | ## 31. 定义一个包内全局字符串变量,下面语法正确的是() 36 | 37 | - A. var str string 38 | - B. str := "" 39 | - C. str = "" 40 | - D. var str = "" 41 | 42 | **答:A、D** 43 | 44 | **解析:** 45 | 46 | B 只支持局部变量声明;C 是赋值,str 必须在这之前已经声明 47 | 48 | ## 32. 下面这段代码输出什么? 49 | 50 | ```go 51 | func hello(i int) { 52 | fmt.Println(i) 53 | } 54 | func main() { 55 | i := 5 56 | defer hello(i) 57 | i = i + 10 58 | } 59 | ``` 60 | 61 | **答:5** 62 | 63 | **解析:** 64 | 65 | 这个例子中,hello() 函数的参数在执行 defer 语句的时候会保存一份副本,在实际调用 hello() 函数时用,所以是 5. 66 | 67 | ## 33. 下面这段代码输出什么? 68 | 69 | ```go 70 | type People struct{} 71 | 72 | func (p *People) ShowA() { 73 | fmt.Println("showA") 74 | p.ShowB() 75 | } 76 | func (p *People) ShowB() { 77 | fmt.Println("showB") 78 | } 79 | 80 | type Teacher struct { 81 | People 82 | } 83 | 84 | func (t *Teacher) ShowB() { 85 | fmt.Println("teacher showB") 86 | } 87 | 88 | func main() { 89 | t := Teacher{} 90 | t.ShowA() 91 | } 92 | ``` 93 | 94 | **答:** 95 | 96 | ```shell 97 | showA 98 | showB 99 | ``` 100 | 101 | **解析:** 102 | 103 | 知识点:结构体嵌套。这道题可以结合第 12 天的第三题一起看,Teacher 没有自己 ShowA(),所以调用内部类型 People 的同名方法,需要注意的是第 5 行代码调用的是 People 自己的 ShowB 方法。 104 | 105 | ## 34. 下面代码输出什么? 106 | 107 | ```go 108 | func main() { 109 | str := "hello" 110 | str[0] = 'x' 111 | fmt.Println(str) 112 | } 113 | ``` 114 | 115 | - A. hello 116 | - B. xello 117 | - C. compilation error 118 | 119 | **答:C** 120 | 121 | **解析:** 122 | 123 | 知识点:常量 124 | 125 | Go 语言中的字符串是只读的。 126 | 127 | ## 35. 下面代码输出什么? 128 | 129 | ```go 130 | func incr(p *int) int { 131 | *p++ 132 | return *p 133 | } 134 | 135 | func main() { 136 | p :=1 137 | incr(&p) 138 | fmt.Println(p) 139 | } 140 | ``` 141 | 142 | - A. 1 143 | - B. 2 144 | - C. 3 145 | 146 | **答:B** 147 | 148 | **解析:** 149 | 150 | 知识点:指针 151 | 152 | incr() 函数里的 p 是 `*int` 类型的指针,指向的是 main() 函数的变量 p 的地址。第 2 行代码是将该地址的值执行一个自增操作,incr() 返回自增后的结果。 153 | 154 | ## 36. 对 add() 函数调用正确的是() 155 | 156 | ```go 157 | func add(args ...int) int { 158 | 159 | sum := 0 160 | for _, arg := range args { 161 | sum += arg 162 | } 163 | return sum 164 | } 165 | ``` 166 | 167 | - A. add(1, 2) 168 | - B. add(1, 3, 7) 169 | - C. add([]int{1, 2}) 170 | - D. add([]int{1, 3, 7}…) 171 | 172 | **答:ABD** 173 | 174 | **解析:** 175 | 176 | 知识点:`可变函数`。 177 | 178 | ## 37. 下面代码下划线处可以填入哪个选项以输出yes nil? 179 | 180 | ```go 181 | func main() { 182 | var s1 []int 183 | var s2 = []int{} 184 | if __ == nil { 185 | fmt.Println("yes nil") 186 | }else{ 187 | fmt.Println("no nil") 188 | } 189 | } 190 | ``` 191 | 192 | - A. s1 193 | - B. s2 194 | - C. s1、s2 都可以 195 | 196 | **答:A** 197 | 198 | **解析:** 199 | 200 | [讨论 #6](https://github.com/yqchilde/Golang-Interview/issues/6) 201 | 202 | 知识点:nil 切片和空切片。 203 | 204 | nil 切片和 nil 相等,一般用来表示一个不存在的切片;空切片和 nil 不相等,表示一个空的集合。 205 | 206 | ## 38. 下面这段代码输出什么? 207 | 208 | ```go 209 | func main() { 210 | i := 65 211 | fmt.Println(string(i)) 212 | } 213 | ``` 214 | 215 | - A. A 216 | - B. 65 217 | - C. compilation error 218 | 219 | **答:A** 220 | 221 | **解析:** 222 | 223 | UTF-8 编码中,十进制数字 65 对应的符号是 A。但是在Goland中会有警告 `Conversion from int to string interprets an integer value as a code point`, 224 | 推荐使用 `var i byte = 65` 或 `var i uint8 = 65` 替代 225 | 226 | ## 39. 下面这段代码输出什么? 227 | 228 | ```go 229 | type A interface { 230 | ShowA() int 231 | } 232 | 233 | type B interface { 234 | ShowB() int 235 | } 236 | 237 | type Work struct { 238 | i int 239 | } 240 | 241 | func (w Work) ShowA() int { 242 | return w.i + 10 243 | } 244 | 245 | func (w Work) ShowB() int { 246 | return w.i + 20 247 | } 248 | 249 | func main() { 250 | c := Work{3} 251 | var a A = c 252 | var b B = c 253 | fmt.Println(a.ShowA()) 254 | fmt.Println(b.ShowB()) 255 | } 256 | ``` 257 | 258 | **答:13 23** 259 | 260 | **解析:** 261 | 262 | 知识点:接口。 263 | 264 | 一种类型实现多个接口,结构体 Work 分别实现了接口 A、B,所以接口变量 a、b 调用各自的方法 ShowA() 和 ShowB(),输出 13、23。 265 | 266 | ## 40. 切片 a、b、c 的长度和容量分别是多少? 267 | 268 | ```go 269 | func main() { 270 | 271 | s := [3]int{1, 2, 3} 272 | a := s[:0] 273 | b := s[:2] 274 | c := s[1:2:cap(s)] 275 | } 276 | ``` 277 | 278 | **答:0 3、2 3、1 2** 279 | 280 | **解析:** 281 | 282 | 知识点:数组或切片的截取操作。 283 | 284 | 截取操作有带 2 个或者 3 个参数,形如:[i:j] 和 [i:j:k],假设截取对象的底层数组长度为 l。在操作符 [i:j] 中,如果 i 省略,默认 0,如果 j 省略,默认底层数组的长度,截取得到的**切片长度和容量计算方法是 j-i、l-i**。操作符 [i:j:k],k 主要是用来限制切片的容量,但是不能大于数组的长度 l,截取得到的**切片长度和容量计算方法是 j-i、k-i**。 285 | 286 | ## 41. 下面代码中 A B 两处应该怎么修改才能顺利编译? 287 | 288 | ```go 289 | func main() { 290 | var m map[string]int //A 291 | m["a"] = 1 292 | if v := m["b"]; v != nil { //B 293 | fmt.Println(v) 294 | } 295 | } 296 | ``` 297 | 298 | **解析:** 299 | 300 | 在 A 处只声明了map m ,并没有分配内存空间,不能直接赋值,需要使用 make(),都提倡使用 make() 或者字面量的方式直接初始化 map。 301 | 302 | B 处,`v,k := m["b"]` 当 key 为 b 的元素不存在的时候,v 会返回值类型对应的零值,k 返回 false。 303 | 304 | ## 42. 下面代码输出什么? 305 | 306 | ```go 307 | type A interface { 308 | ShowA() int 309 | } 310 | 311 | type B interface { 312 | ShowB() int 313 | } 314 | 315 | type Work struct { 316 | i int 317 | } 318 | 319 | func (w Work) ShowA() int { 320 | return w.i + 10 321 | } 322 | 323 | func (w Work) ShowB() int { 324 | return w.i + 20 325 | } 326 | 327 | func main() { 328 | c := Work{3} 329 | var a A = c 330 | var b B = c 331 | fmt.Println(a.ShowB()) 332 | fmt.Println(b.ShowA()) 333 | } 334 | ``` 335 | 336 | - A. 23 13 337 | - B. compilation error 338 | 339 | **答:B** 340 | 341 | **解析:** 342 | 343 | 知识点:接口的静态类型。 344 | 345 | a、b 具有相同的动态类型和动态值,分别是结构体 work 和 {3};a 的静态类型是 A,b 的静态类型是 B,接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),编译报错。看下编译错误: 346 | 347 | ```shell 348 | a.ShowB undefined (type A has no field or method ShowB) 349 | b.ShowA undefined (type B has no field or method ShowA) 350 | ``` 351 | 352 | ## 43. 下面代码中,x 已声明,y 没有声明,判断每条语句的对错。 353 | 354 | ```go 355 | 1. x, _ := f() 356 | 2. x, _ = f() 357 | 3. x, y := f() 358 | 4. x, y = f() 359 | ``` 360 | 361 | **答:错、对、对、错** 362 | 363 | **解析:** 364 | 365 | 知识点:变量的声明。 366 | 367 | 1.错,x 已经声明,不能使用 :=;2.对;3.对,当多值赋值时,:= 左边的变量无论声明与否都可以;4.错,y 没有声明。 368 | 369 | ## 44. 下面代码输出什么? 370 | 371 | ```go 372 | func increaseA() int { 373 | var i int 374 | defer func() { 375 | i++ 376 | }() 377 | return i 378 | } 379 | 380 | func increaseB() (r int) { 381 | defer func() { 382 | r++ 383 | }() 384 | return r 385 | } 386 | 387 | func main() { 388 | fmt.Println(increaseA()) 389 | fmt.Println(increaseB()) 390 | } 391 | ``` 392 | 393 | - A. 1 1 394 | - B. 0 1 395 | - C. 1 0 396 | - D. 0 0 397 | 398 | **答:B** 399 | 400 | **解析:** 401 | 402 | 知识点:defer、返回值。 403 | 404 | 注意一下,increaseA() 的返回参数是匿名,increaseB() 是具名。关于 defer 与返回值的知识点,后面我会写篇文章详细分析,到时候可以看下文章的讲解。 405 | 406 | ## 45. 下面代码输出什么? 407 | 408 | ```go 409 | type A interface { 410 | ShowA() int 411 | } 412 | 413 | type B interface { 414 | ShowB() int 415 | } 416 | 417 | type Work struct { 418 | i int 419 | } 420 | 421 | func (w Work) ShowA() int { 422 | return w.i + 10 423 | } 424 | 425 | func (w Work) ShowB() int { 426 | return w.i + 20 427 | } 428 | 429 | func main() { 430 | var a A = Work{3} 431 | s := a.(Work) 432 | fmt.Println(s.ShowA()) 433 | fmt.Println(s.ShowB()) 434 | } 435 | ``` 436 | 437 | - A. 13 23 438 | - B. compilation error 439 | 440 | **答:A** 441 | 442 | **解析:** 443 | 444 | 知识点:类型断言。 445 | 446 | ## 46. f1()、f2()、f3() 函数分别返回什么? 447 | 448 | ```go 449 | func f1() (r int) { 450 | defer func() { 451 | r++ 452 | }() 453 | return 0 454 | } 455 | 456 | func f2() (r int) { 457 | t := 5 458 | defer func() { 459 | t = t + 5 460 | }() 461 | return t 462 | } 463 | 464 | func f3() (r int) { 465 | defer func(r int) { 466 | r = r + 5 467 | }(r) 468 | return 1 469 | } 470 | ``` 471 | 472 | **答:1 5 1** 473 | 474 | **解析:** 475 | 476 | 知识点:`defer`函数的执行顺序。 477 | 478 | ## 47. 下面代码段输出什么? 479 | 480 | ```go 481 | type Person struct { 482 | age int 483 | } 484 | 485 | func main() { 486 | person := &Person{28} 487 | 488 | // 1. 489 | defer fmt.Println(person.age) 490 | 491 | // 2. 492 | defer func(p *Person) { 493 | fmt.Println(p.age) 494 | }(person) 495 | 496 | // 3. 497 | defer func() { 498 | fmt.Println(person.age) 499 | }() 500 | 501 | person.age = 29 502 | } 503 | ``` 504 | 505 | **答:29 29 28** 506 | 507 | **解析:** 508 | 509 | 知识点:`defer`函数的执行顺序。 510 | 511 | 变量 person 是一个指针变量 。 512 | 513 | 1.person.age 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28; 514 | 515 | 2.defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29; 516 | 517 | 3.闭包引用,输出 29; 518 | 519 | 又由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 29 28。 520 | 521 | ## 48. 下面这段代码正确的输出是什么? 522 | 523 | ```go 524 | func f() { 525 | defer fmt.Println("D") 526 | fmt.Println("F") 527 | } 528 | 529 | func main() { 530 | f() 531 | fmt.Println("M") 532 | } 533 | ``` 534 | 535 | - A. F M D 536 | - B. D F M 537 | - C. F D M 538 | 539 | **答:C** 540 | 541 | **解析:** 542 | 543 | 被调用函数里的 defer 语句在返回之前就会被执行,所以输出顺序是 F D M。 544 | 545 | ## 49. 下面代码输出什么? 546 | 547 | ```go 548 | type Person struct { 549 | age int 550 | } 551 | 552 | func main() { 553 | person := &Person{28} 554 | 555 | // 1. 556 | defer fmt.Println(person.age) 557 | 558 | // 2. 559 | defer func(p *Person) { 560 | fmt.Println(p.age) 561 | }(person) 562 | 563 | // 3. 564 | defer func() { 565 | fmt.Println(person.age) 566 | }() 567 | 568 | person = &Person{29} 569 | } 570 | ``` 571 | 572 | **答:29 28 28** 573 | 574 | **解析:** 575 | 576 | 知识点:`defer`函数的执行顺序。 577 | 578 | 这道题在第 `47` 题目的基础上做了一点点小改动,前一题最后一行代码 `person.age = 29` 是修改引用对象的成员 age,这题最后一行代码 `person = &Person{29}` 是修改引用对象本身,来看看有什么区别。 579 | 580 | 1处.person.age 这一行代码跟之前含义是一样的,此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28; 581 | 582 | 2处.defer 缓存的是结构体 Person{28} 的地址,这个地址指向的结构体没有被改变,最后 defer 语句后面的函数执行的时候取出仍是 28; 583 | 584 | 3处.闭包引用,person 的值已经被改变,指向结构体 `Person{29}`,所以输出 29. 585 | 586 | 由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 28 28。 587 | 588 | ## 50. 下面的两个切片声明中有什么区别?哪个更可取? 589 | 590 | ```go 591 | A. var a []int 592 | B. a := []int{} 593 | ``` 594 | 595 | **答:29 28 28** 596 | 597 | **解析:** 598 | 599 | A 声明的是 nil 切片;B 声明的是长度和容量都为 0 的空切片。第一种切片声明不会分配内存,优先选择。 600 | 601 | ## 51. A、B、C、D 哪些选项有语法错误? 602 | 603 | ```go 604 | type S struct { 605 | } 606 | 607 | func f(x interface{}) { 608 | } 609 | 610 | func g(x *interface{}) { 611 | } 612 | 613 | func main() { 614 | s := S{} 615 | p := &s 616 | f(s) //A 617 | g(s) //B 618 | f(p) //C 619 | g(p) //D 620 | } 621 | ``` 622 | 623 | **答:BD** 624 | 625 | **解析:** 626 | 627 | 函数参数为 interface{} 时可以接收任何类型的参数,包括用户自定义类型等,即使是接收指针类型也用 interface{},而不是使用 *interface{}。 628 | 629 | ## 52. 下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果? 630 | 631 | ```go 632 | type S struct { 633 | m string 634 | } 635 | 636 | func f() *S { 637 | return __ //A 638 | } 639 | 640 | func main() { 641 | p := __ //B 642 | fmt.Println(p.m) //print "foo" 643 | } 644 | ``` 645 | 646 | **答:** 647 | 648 | ```go 649 | A. &S{"foo"} 650 | B. *f() 或者 f() 651 | ``` 652 | 653 | **解析:** 654 | 655 | f() 函数返回参数是指针类型,所以可以用 & 取结构体的指针;B 处,如果填`*f()`,则 p 是 S 类型;如果填 `f()`,则 p 是 *S 类型,不过都可以使用 `p.m`取得结构体的成员。 656 | 657 | ## 53. 下面的代码有几处语法问题,各是什么? 658 | 659 | ```go 660 | package main 661 | import ( 662 | "fmt" 663 | ) 664 | func main() { 665 | var x string = nil 666 | if x == nil { 667 | x = "default" 668 | } 669 | fmt.Println(x) 670 | } 671 | ``` 672 | 673 | **解析:** 674 | 675 | 两个地方有语法问题。golang 的字符串类型是不能赋值 nil 的,也不能跟 nil 比较。 676 | 677 | ## 54. return 之后的 defer 语句会执行吗,下面这段代码输出什么? 678 | 679 | ```go 680 | var a bool = true 681 | func main() { 682 | defer func(){ 683 | fmt.Println("1") 684 | }() 685 | if a == true { 686 | fmt.Println("2") 687 | return 688 | } 689 | defer func(){ 690 | fmt.Println("3") 691 | }() 692 | } 693 | ``` 694 | 695 | **答:2 1** 696 | 697 | **解析:** 698 | 699 | defer 关键字后面的函数或者方法想要执行必须先注册,return 之后的 defer 是不能注册的, 也就不能执行后面的函数或方法。 700 | 701 | ## 55. 下面这段代码输出什么?为什么? 702 | 703 | ```go 704 | func main() { 705 | 706 | s1 := []int{1, 2, 3} 707 | s2 := s1[1:] 708 | s2[1] = 4 709 | fmt.Println(s1) 710 | s2 = append(s2, 5, 6, 7) 711 | fmt.Println(s1) 712 | } 713 | ``` 714 | 715 | **答:** 716 | 717 | ```shell 718 | [1 2 4] 719 | [1 2 4] 720 | ``` 721 | 722 | **解析:** 723 | 724 | 我们知道,golang 中切片底层的数据结构是数组。当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组,这会导致 s2[1] = 4 语句影响 s1。 725 | 726 | 而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1。 727 | 728 | 但是为什么对 s2 赋值后影响的却是 s1 的第三个元素呢?这是因为切片 s2 是从数组的第二个元素开始,s2 索引为 1 的元素对应的是 s1 索引为 2 的元素。 729 | 730 | ![golang](https://mmbiz.qpic.cn/mmbiz_png/zVM9HMBJAjODRPcZwkMU3iakW3b9edoKcOYtcOyl7mLR3yibLianbibG0uLC5KOU64obJO6AqOGJ8FvQ3xONINavng/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 731 | 732 | ## 56. 下面选项正确的是? 733 | 734 | ```go 735 | func main() { 736 | if a := 1; false { 737 | } else if b := 2; false { 738 | } else { 739 | println(a, b) 740 | } 741 | } 742 | ``` 743 | 744 | - A. 1 2 745 | - B. compilation error 746 | 747 | **答:A** 748 | 749 | **解析:** 750 | 751 | 知识点:代码块和变量作用域。 752 | 753 | ## 57. 下面这段代码输出什么? 754 | 755 | ```go 756 | func main() { 757 | m := map[int]string{0:"zero",1:"one"} 758 | for k,v := range m { 759 | fmt.Println(k,v) 760 | } 761 | } 762 | ``` 763 | 764 | **答:** 765 | 766 | ```shell 767 | 0 zero 768 | 1 one 769 | // 或者 770 | 1 one 771 | 0 zero 772 | ``` 773 | 774 | **解析:** 775 | 776 | map 的输出是无序的。 777 | 778 | ## 58. 下面这段代码输出什么? 779 | 780 | ```go 781 | func main() { 782 | a := 1 783 | b := 2 784 | defer calc("1", a, calc("10", a, b)) 785 | a = 0 786 | defer calc("2", a, calc("20", a, b)) 787 | b = 1 788 | } 789 | 790 | func calc(index string, a, b int) int { 791 | ret := a + b 792 | fmt.Println(index, a, b, ret) 793 | return ret 794 | } 795 | ``` 796 | 797 | **答:** 798 | 799 | ```shell 800 | 10 1 2 3 801 | 20 0 2 2 802 | 2 0 2 2 803 | 1 1 3 4 804 | ``` 805 | 806 | **解析:** 807 | 808 | 程序执行到 main() 函数三行代码的时候,会先执行 calc() 函数的 b 参数,即:`calc("10",a,b)`,输出:10 1 2 3,得到值 3,因为 809 | defer 定义的函数是延迟函数,故 calc("1",1,3) 会被延迟执行; 810 | 811 | 程序执行到第五行的时候,同样先执行 calc("20",a,b) 输出:20 0 2 2 得到值 2,同样将 calc("2",0,2) 延迟执行; 812 | 813 | 程序执行到末尾的时候,按照栈先进后出的方式依次执行:calc("2",0,2),calc("1",1,3),则就依次输出:2 0 2 2,1 1 3 4。 814 | 815 | ## 59. 下面这段代码输出什么?为什么? 816 | 817 | ```go 818 | func (i int) PrintInt () { 819 | fmt.Println(i) 820 | } 821 | 822 | func main() { 823 | var i int = 1 824 | i.PrintInt() 825 | } 826 | ``` 827 | 828 | - A. 1 829 | - B. compilation error 830 | 831 | **答:B** 832 | 833 | **解析:** 834 | 835 | **基于类型创建的方法必须定义在同一个包内**,上面的代码基于 int 类型创建了 PrintInt() 方法,由于 int 类型和方法 PrintInt() 定义在不同的包内,所以编译出错。 836 | 837 | 解决的办法可以定义一种新的类型: 838 | 839 | ```go 840 | type Myint int 841 | 842 | func (i Myint) PrintInt () { 843 | fmt.Println(i) 844 | } 845 | 846 | func main() { 847 | var i Myint = 1 848 | i.PrintInt() 849 | } 850 | ``` 851 | 852 | ## 60. 下面这段代码输出什么?为什么? 853 | 854 | ```go 855 | type People interface { 856 | Speak(string) string 857 | } 858 | 859 | type Student struct{} 860 | 861 | func (stu *Student) Speak(think string) (talk string) { 862 | if think == "speak" { 863 | talk = "speak" 864 | } else { 865 | talk = "hi" 866 | } 867 | return 868 | } 869 | 870 | func main() { 871 | var peo People = Student{} 872 | think := "speak" 873 | fmt.Println(peo.Speak(think)) 874 | } 875 | ``` 876 | 877 | - A. speak 878 | - B. compilation error 879 | 880 | **答:B** 881 | 882 | **解析:** 883 | 884 | 编译错误 `Student does not implement People (Speak method has pointer receiver)`,值类型 `Student` 没有实现接口的 `Speak()` 方法,而是指针类型 `*Student` 实现该方法。 -------------------------------------------------------------------------------- /61-90.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [61\. 下面这段代码输出什么?](#61-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 4 | > * [62\. 下面这段代码输出什么?为什么?](#62-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 5 | > * [63\. 下面这段代码输出什么?](#63-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 6 | > * [64\. 下面代码输出什么?](#64-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 7 | > * [65\. 下面的代码有什么问题?](#65-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 8 | > * [66\. 下面这段代码输出什么?如果编译错误的话,为什么?](#66-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E7%BC%96%E8%AF%91%E9%94%99%E8%AF%AF%E7%9A%84%E8%AF%9D%E4%B8%BA%E4%BB%80%E4%B9%88) 9 | > * [67\. 下面这段代码能否正常结束?](#67-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E6%AD%A3%E5%B8%B8%E7%BB%93%E6%9D%9F) 10 | > * [68\. 下面这段代码输出什么?为什么?](#68-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88) 11 | > * [69\. 下面这段代码输出什么?](#69-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 12 | > * [70\. 下面这段代码输出什么?](#70-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 13 | > * [71\. 下面这段代码输出什么?](#71-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 14 | > * [72\. 下面这段代码输出什么?](#72-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [73\. 下面这段代码输出结果正确正确吗?](#73-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C%E6%AD%A3%E7%A1%AE%E6%AD%A3%E7%A1%AE%E5%90%97) 16 | > * [74\. 下面代码里的 counter 的输出值?](#74-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E9%87%8C%E7%9A%84-counter-%E7%9A%84%E8%BE%93%E5%87%BA%E5%80%BC) 17 | > * [75\. 关于协程,下面说法正确是()](#75-%E5%85%B3%E4%BA%8E%E5%8D%8F%E7%A8%8B%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E6%98%AF) 18 | > * [76\. 关于循环语句,下面说法正确的有()](#76-%E5%85%B3%E4%BA%8E%E5%BE%AA%E7%8E%AF%E8%AF%AD%E5%8F%A5%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%9C%89) 19 | > * [77\. 下面代码输出正确的是?](#77-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 20 | > * [78\. 关于类型转化,下面选项正确的是?](#78-%E5%85%B3%E4%BA%8E%E7%B1%BB%E5%9E%8B%E8%BD%AC%E5%8C%96%E4%B8%8B%E9%9D%A2%E9%80%89%E9%A1%B9%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 21 | > * [79\. 关于switch语句,下面说法正确的有?](#79-%E5%85%B3%E4%BA%8Eswitch%E8%AF%AD%E5%8F%A5%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%9C%89) 22 | > * [80\. 如果 Add() 函数的调用代码为:](#80-%E5%A6%82%E6%9E%9C-add-%E5%87%BD%E6%95%B0%E7%9A%84%E8%B0%83%E7%94%A8%E4%BB%A3%E7%A0%81%E4%B8%BA) 23 | > * [81\. 关于 bool 变量 b 的赋值,下面错误的用法是?](#81-%E5%85%B3%E4%BA%8E-bool-%E5%8F%98%E9%87%8F-b-%E7%9A%84%E8%B5%8B%E5%80%BC%E4%B8%8B%E9%9D%A2%E9%94%99%E8%AF%AF%E7%9A%84%E7%94%A8%E6%B3%95%E6%98%AF) 24 | > * [82\. 关于变量的自增和自减操作,下面语句正确的是?](#82-%E5%85%B3%E4%BA%8E%E5%8F%98%E9%87%8F%E7%9A%84%E8%87%AA%E5%A2%9E%E5%92%8C%E8%87%AA%E5%87%8F%E6%93%8D%E4%BD%9C%E4%B8%8B%E9%9D%A2%E8%AF%AD%E5%8F%A5%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 25 | > * [83\. 关于GetPodAction定义,下面赋值正确的是](#83-%E5%85%B3%E4%BA%8Egetpodaction%E5%AE%9A%E4%B9%89%E4%B8%8B%E9%9D%A2%E8%B5%8B%E5%80%BC%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 26 | > * [84\. 关于函数声明,下面语法正确的是?](#84-%E5%85%B3%E4%BA%8E%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 27 | > * [85\. 关于整型切片的初始化,下面正确的是?](#85-%E5%85%B3%E4%BA%8E%E6%95%B4%E5%9E%8B%E5%88%87%E7%89%87%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8B%E9%9D%A2%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 28 | > * [86\. 下面代码会触发异常吗?请说明。](#86-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E4%BC%9A%E8%A7%A6%E5%8F%91%E5%BC%82%E5%B8%B8%E5%90%97%E8%AF%B7%E8%AF%B4%E6%98%8E) 29 | > * [87\. 关于channel的特性,下面说法正确的是?](#87-%E5%85%B3%E4%BA%8Echannel%E7%9A%84%E7%89%B9%E6%80%A7%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 30 | > * [88\. 下面代码有什么问题?](#88-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 31 | > * [89\. 下面代码能否编译通过?如果通过,输出什么?](#89-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%A6%82%E6%9E%9C%E9%80%9A%E8%BF%87%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 32 | > * [90\. 关于异常的触发,下面说法正确的是?](#90-%E5%85%B3%E4%BA%8E%E5%BC%82%E5%B8%B8%E7%9A%84%E8%A7%A6%E5%8F%91%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 33 | > * [91\. 91-120题](https://github.com/yqchilde/Golang-Interview/blob/master/91-120.md) 34 | 35 | ## 61. 下面这段代码输出什么? 36 | 37 | ```go 38 | const ( 39 | a = iota 40 | b = iota 41 | ) 42 | const ( 43 | name = "name" 44 | c = iota 45 | d = iota 46 | ) 47 | func main() { 48 | fmt.Println(a) 49 | fmt.Println(b) 50 | fmt.Println(c) 51 | fmt.Println(d) 52 | } 53 | ``` 54 | 55 | **答:0 1 1 2** 56 | 57 | **解析:** 58 | 59 | 知识点:iota 的用法。 60 | 61 | iota 是 golang 语言的常量计数器,只能在常量的表达式中使用。 62 | 63 | iota 在 const 关键字出现时将被重置为0,const中每新增一行常量声明将使 iota 计数一次。 64 | 65 | ## 62. 下面这段代码输出什么?为什么? 66 | 67 | ```go 68 | type People interface { 69 | Show() 70 | } 71 | 72 | type Student struct{} 73 | 74 | func (stu *Student) Show() { 75 | 76 | } 77 | 78 | func main() { 79 | 80 | var s *Student 81 | if s == nil { 82 | fmt.Println("s is nil") 83 | } else { 84 | fmt.Println("s is not nil") 85 | } 86 | var p People = s 87 | if p == nil { 88 | fmt.Println("p is nil") 89 | } else { 90 | fmt.Println("p is not nil") 91 | } 92 | } 93 | ``` 94 | 95 | **答:`s is nil` 和 `p is not nil`** 96 | 97 | **解析:** 98 | 99 | 这道题会不会有点诧异,我们分配给变量 p 的值明明是 nil,然而 p 却不是 nil。记住一点,**当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil**。上面的代码,给变量 p 赋值之后,p 的动态值是 nil,但是动态类型却是 *Student,是一个 nil 指针,所以相等条件不成立。 100 | 101 | ## 63. 下面这段代码输出什么? 102 | 103 | ```go 104 | type Direction int 105 | 106 | const ( 107 | North Direction = iota 108 | East 109 | South 110 | West 111 | ) 112 | 113 | func (d Direction) String() string { 114 | return [...]string{"North", "East", "South", "West"}[d] 115 | } 116 | 117 | func main() { 118 | fmt.Println(South) 119 | } 120 | ``` 121 | 122 | **答:South** 123 | 124 | **解析:** 125 | 126 | 知识点:iota 的用法、类型的 String() 方法。 127 | 128 | ## 64. 下面代码输出什么? 129 | 130 | ```go 131 | type Math struct { 132 | x, y int 133 | } 134 | 135 | var m = map[string]Math{ 136 | "foo": Math{2, 3}, 137 | } 138 | 139 | func main() { 140 | m["foo"].x = 4 141 | fmt.Println(m["foo"].x) 142 | } 143 | ``` 144 | 145 | - A. 4 146 | - B. compilation error 147 | 148 | **答:B** 149 | 150 | **解析:** 151 | 152 | 编译报错 `cannot assign to struct field m["foo"].x in map`。错误原因:对于类似 `X = Y`的赋值操作,必须知道 `X` 的地址,才能够将 `Y` 的值赋给 `X`,但 go 中的 map 的 value 本身是不可寻址的。 153 | 154 | 有两个解决办法: 155 | 156 | 1. **使用临时变量** 157 | 158 | ```go 159 | type Math struct { 160 | x, y int 161 | } 162 | 163 | var m = map[string]Math{ 164 | "foo": Math{2, 3}, 165 | } 166 | 167 | func main() { 168 | tmp := m["foo"] 169 | tmp.x = 4 170 | m["foo"] = tmp 171 | fmt.Println(m["foo"].x) 172 | } 173 | ``` 174 | 175 | 2. **修改数据结构** 176 | 177 | ```go 178 | type Math struct { 179 | x, y int 180 | } 181 | 182 | var m = map[string]*Math{ 183 | "foo": &Math{2, 3}, 184 | } 185 | 186 | func main() { 187 | m["foo"].x = 4 188 | fmt.Println(m["foo"].x) 189 | fmt.Printf("%#v", m["foo"]) // %#v 格式化输出详细信息 190 | } 191 | ``` 192 | 193 | ## 65. 下面的代码有什么问题? 194 | 195 | ```go 196 | func main() { 197 | fmt.Println([...]int{1} == [2]int{1}) 198 | fmt.Println([]int{1} == []int{1}) 199 | } 200 | ``` 201 | 202 | **答:有两处错误** 203 | 204 | **解析:** 205 | 206 | - go 中不同类型是不能比较的,而数组长度是数组类型的一部分,所以 `[…]int{1}` 和 `[2]int{1}` 是两种不同的类型,不能比较; 207 | - 切片是不能比较的; 208 | 209 | ## 66. 下面这段代码输出什么?如果编译错误的话,为什么? 210 | 211 | ```go 212 | var p *int 213 | 214 | func foo() (*int, error) { 215 | var i int = 5 216 | return &i, nil 217 | } 218 | 219 | func bar() { 220 | //use p 221 | fmt.Println(*p) 222 | } 223 | 224 | func main() { 225 | p, err := foo() 226 | if err != nil { 227 | fmt.Println(err) 228 | return 229 | } 230 | bar() 231 | fmt.Println(*p) 232 | } 233 | ``` 234 | 235 | - A. 5 5 236 | - B. runtime error 237 | 238 | **答:B** 239 | 240 | **解析:** 241 | 242 | 知识点:变量作用域。 243 | 244 | 问题出在操作符`:=`,对于使用`:=`定义的变量,如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到`bar()`时程序,全局变量 p 依然还是 nil,程序随即 Crash。 245 | 246 | 正确的做法是将 main() 函数修改为: 247 | 248 | ```go 249 | func main() { 250 | var err error 251 | p, err = foo() 252 | if err != nil { 253 | fmt.Println(err) 254 | return 255 | } 256 | bar() 257 | fmt.Println(*p) 258 | } 259 | ``` 260 | 261 | ## 67. 下面这段代码能否正常结束? 262 | 263 | ```go 264 | func main() { 265 | v := []int{1, 2, 3} 266 | for i := range v { 267 | v = append(v, i) 268 | } 269 | } 270 | ``` 271 | 272 | **答:不会出现死循环,能正常结束** 273 | 274 | **解析:** 275 | 276 | 循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。 277 | 278 | ## 68. 下面这段代码输出什么?为什么? 279 | 280 | ```go 281 | func main() { 282 | 283 | var m = [...]int{1, 2, 3} 284 | 285 | for i, v := range m { 286 | go func() { 287 | fmt.Println(i, v) 288 | }() 289 | } 290 | 291 | time.Sleep(time.Second * 3) 292 | } 293 | ``` 294 | 295 | **答:** 296 | 297 | ```shell 298 | 2 3 299 | 2 3 300 | 2 3 301 | ``` 302 | 303 | **解析:** 304 | 305 | for range 使用短变量声明(:=)的形式迭代变量,需要注意的是,变量 i、v 在每次循环体中都会被重用,而不是重新声明。 306 | 307 | 各个 goroutine 中输出的 i、v 值都是 for range 循环结束后的 i、v 最终值,而不是各个goroutine启动时的i, v值。可以理解为闭包引用,使用的是上下文环境的值。 308 | 309 | 两种可行的 fix 方法: 310 | 311 | 1. **使用函数传递** 312 | 313 | ```go 314 | for i, v := range m { 315 | go func(i,v int) { 316 | fmt.Println(i, v) 317 | }(i,v) 318 | } 319 | ``` 320 | 321 | 322 | 323 | 2. **使用临时变量保留当前值** 324 | 325 | ```go 326 | for i, v := range m { 327 | i := i // 这里的 := 会重新声明变量,而不是重用 328 | v := v 329 | go func() { 330 | fmt.Println(i, v) 331 | }() 332 | } 333 | ``` 334 | 335 | 336 | 337 | ## 69. 下面这段代码输出什么? 338 | 339 | ```go 340 | func f(n int) (r int) { 341 | defer func() { 342 | r += n 343 | recover() 344 | }() 345 | 346 | var f func() 347 | 348 | defer f() 349 | f = func() { 350 | r += 2 351 | } 352 | return n + 1 353 | } 354 | 355 | func main() { 356 | fmt.Println(f(3)) 357 | } 358 | ``` 359 | 360 | **答:7** 361 | 362 | **解析:** 363 | 364 | 第一步执行`r = n +1`,接着执行第二个 defer,由于此时 f() 未定义,引发异常,随即执行第一个 defer,异常被 recover(),程序正常执行,最后 return。 365 | 366 | ## 70. 下面这段代码输出什么? 367 | 368 | ```go 369 | func main() { 370 | var a = [5]int{1, 2, 3, 4, 5} 371 | var r [5]int 372 | 373 | for i, v := range a { 374 | if i == 0 { 375 | a[1] = 12 376 | a[2] = 13 377 | } 378 | r[i] = v 379 | } 380 | fmt.Println("r = ", r) 381 | fmt.Println("a = ", a) 382 | } 383 | ``` 384 | 385 | **答:** 386 | 387 | ```shell 388 | r = [1 2 3 4 5] 389 | a = [1 12 13 4 5] 390 | ``` 391 | 392 | **解析:** 393 | 394 | range 表达式是副本参与循环,就是说例子中参与循环的是 a 的副本,而不是真正的 a。就这个例子来说,假设 b 是 a 的副本,则 range 循环代码是这样的: 395 | 396 | ```go 397 | for i, v := range b { 398 | if i == 0 { 399 | a[1] = 12 400 | a[2] = 13 401 | } 402 | r[i] = v 403 | } 404 | ``` 405 | 406 | 因此无论 a 被如何修改,其副本 b 依旧保持原值,并且参与循环的是 b,因此 v 从 b 中取出的仍旧是 a 的原值,而非修改后的值。 407 | 408 | 如果想要 r 和 a 一样输出,修复办法: 409 | 410 | ```go 411 | func main() { 412 | var a = [5]int{1, 2, 3, 4, 5} 413 | var r [5]int 414 | 415 | for i, v := range &a { 416 | if i == 0 { 417 | a[1] = 12 418 | a[2] = 13 419 | } 420 | r[i] = v 421 | } 422 | fmt.Println("r = ", r) 423 | fmt.Println("a = ", a) 424 | } 425 | ``` 426 | 427 | 输出: 428 | 429 | ```shell 430 | r = [1 12 13 4 5] 431 | a = [1 12 13 4 5] 432 | ``` 433 | 434 | 修复代码中,使用 *[5]int 作为 range 表达式,其副本依旧是一个指向原数组 a 的指针,因此后续所有循环中均是 &a 指向的原数组亲自参与的,因此 v 能从 &a 指向的原数组中取出 a 修改后的值。 435 | 436 | ## 71. 下面这段代码输出什么? 437 | 438 | ```go 439 | func change(s ...int) { 440 | s = append(s,3) 441 | } 442 | 443 | func main() { 444 | slice := make([]int,5,5) 445 | slice[0] = 1 446 | slice[1] = 2 447 | change(slice...) 448 | fmt.Println(slice) 449 | change(slice[0:2]...) 450 | fmt.Println(slice) 451 | } 452 | ``` 453 | 454 | **答:** 455 | 456 | ```shell 457 | [1 2 0 0 0] 458 | [1 2 3 0 0] 459 | ``` 460 | 461 | **解析:** 462 | 463 | 知识点:可变函数、append()操作。 464 | 465 | Go 提供的语法糖`...`,可以将 slice 传进可变函数,不会创建新的切片。第一次调用 change() 时,append() 操作使切片底层数组发生了扩容,原 slice 的底层数组不会改变;第二次调用change() 函数时,使用了操作符`[i,j]`获得一个新的切片,假定为 slice1,它的底层数组和原切片底层数组是重合的,不过 slice1 的长度、容量分别是 2、5,所以在 change() 函数中对 slice1 底层数组的修改会影响到原切片。 466 | 467 | ![golang](https://mmbiz.qpic.cn/mmbiz_png/zVM9HMBJAjNBQ3DzCQUrUbz7JAjAr57j8dQg8FHl2ic4oE0iajKxDSsgGxabGt1wJJEl9eHj76rE8XwcRXzXGorQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 468 | 469 | ## 72. 下面这段代码输出什么? 470 | 471 | ```go 472 | func main() { 473 | var a = []int{1, 2, 3, 4, 5} 474 | var r [5]int 475 | 476 | for i, v := range a { 477 | if i == 0 { 478 | a[1] = 12 479 | a[2] = 13 480 | } 481 | r[i] = v 482 | } 483 | fmt.Println("r = ", r) 484 | fmt.Println("a = ", a) 485 | } 486 | ``` 487 | 488 | **答:** 489 | 490 | ```shell 491 | r = [1 12 13 4 5] 492 | a = [1 12 13 4 5] 493 | ``` 494 | 495 | 496 | 497 | **解析:** 498 | 499 | 切片在 go 的内部结构有一个指向底层数组的指针,当 range 表达式发生复制时,副本的指针依旧指向原底层数组,所以对切片的修改都会反应到底层数组上,所以通过 v 可以获得修改后的数组元素。 500 | 501 | ## 73. 下面这段代码输出结果正确正确吗? 502 | 503 | ```go 504 | type Foo struct { 505 | bar string 506 | } 507 | func main() { 508 | s1 := []Foo{ 509 | {"A"}, 510 | {"B"}, 511 | {"C"}, 512 | } 513 | s2 := make([]*Foo, len(s1)) 514 | for i, value := range s1 { 515 | s2[i] = &value 516 | } 517 | fmt.Println(s1[0], s1[1], s1[2]) 518 | fmt.Println(s2[0], s2[1], s2[2]) 519 | } 520 | 输出: 521 | {A} {B} {C} 522 | &{A} &{B} &{C} 523 | ``` 524 | 525 | **答:s2 的输出结果错误** 526 | 527 | **解析:** 528 | 529 | s2 的输出是 `&{C} &{C} &{C}`,for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2 输出的时候显示出了三个 &{c}。 530 | 531 | 可行的解决办法如下: 532 | 533 | ```go 534 | for i := range s1 { 535 | s2[i] = &s1[i] 536 | } 537 | ``` 538 | 539 | 540 | 541 | ## 74. 下面代码里的 counter 的输出值? 542 | 543 | ```go 544 | func main() { 545 | 546 | var m = map[string]int{ 547 | "A": 21, 548 | "B": 22, 549 | "C": 23, 550 | } 551 | counter := 0 552 | for k, v := range m { 553 | if counter == 0 { 554 | delete(m, "A") 555 | } 556 | counter++ 557 | fmt.Println(k, v) 558 | } 559 | fmt.Println("counter is ", counter) 560 | } 561 | ``` 562 | 563 | - A. 2 564 | - B. 3 565 | - C. 2 或 3 566 | 567 | **答:C** 568 | 569 | **解析:** 570 | 571 | for range map 是无序的,如果第一次循环到 A,则输出 3;否则输出 2。 572 | 573 | ## 75. 关于协程,下面说法正确是() 574 | 575 | - A. 协程和线程都可以实现程序的并发执行; 576 | - B. 线程比协程更轻量级; 577 | - C. 协程不存在死锁问题; 578 | - D. 通过 channel 来进行协程间的通信; 579 | 580 | **答:A D** 581 | 582 | ## 76. 关于循环语句,下面说法正确的有() 583 | 584 | - A. 循环语句既支持 for 关键字,也支持 while 和 do-while; 585 | - B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异; 586 | - C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环; 587 | - D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量; 588 | 589 | **答:C D** 590 | 591 | ## 77. 下面代码输出正确的是? 592 | 593 | ```go 594 | func main() { 595 | i := 1 596 | s := []string{"A", "B", "C"} 597 | i, s[i-1] = 2, "Z" 598 | fmt.Printf("s: %v \n", s) 599 | } 600 | ``` 601 | 602 | - A. s: [Z,B,C] 603 | - B. s: [A,Z,C] 604 | 605 | **答:A** 606 | 607 | **解析:** 608 | 609 | 知识点:多重赋值。 610 | 611 | 多重赋值分为两个步骤,有先后顺序: 612 | 613 | - 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式; 614 | - 赋值; 615 | 616 | 所以本例,会先计算 s[i-1],等号右边是两个表达式是常量,所以赋值运算等同于 `i, s[0] = 2, "Z"`。 617 | 618 | ## 78. 关于类型转化,下面选项正确的是? 619 | 620 | ```go 621 | A. 622 | type MyInt int 623 | var i int = 1 624 | var j MyInt = i 625 | 626 | B. 627 | type MyInt int 628 | var i int = 1 629 | var j MyInt = (MyInt)i 630 | 631 | C. 632 | type MyInt int 633 | var i int = 1 634 | var j MyInt = MyInt(i) 635 | 636 | D. 637 | type MyInt int 638 | var i int = 1 639 | var j MyInt = i.(MyInt) 640 | ``` 641 | 642 | **答:C** 643 | 644 | **解析:** 645 | 646 | 知识点:强制类型转化 647 | 648 | ## 79. 关于switch语句,下面说法正确的有? 649 | 650 | - A. 条件表达式必须为常量或者整数; 651 | - B. 单个case中,可以出现多个结果选项; 652 | - C. 需要用break来明确退出一个case; 653 | - D. 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case; 654 | 655 | **答:B D** 656 | 657 | ## 80. 如果 Add() 函数的调用代码为: 658 | 659 | ```go 660 | func main() { 661 | var a Integer = 1 662 | var b Integer = 2 663 | var i interface{} = &a 664 | sum := i.(*Integer).Add(b) 665 | fmt.Println(sum) 666 | } 667 | ``` 668 | 669 | 则Add函数定义正确的是() 670 | 671 | ```go 672 | A. 673 | type Integer int 674 | func (a Integer) Add(b Integer) Integer { 675 | return a + b 676 | } 677 | 678 | B. 679 | type Integer int 680 | func (a Integer) Add(b *Integer) Integer { 681 | return a + *b 682 | } 683 | 684 | C. 685 | type Integer int 686 | func (a *Integer) Add(b Integer) Integer { 687 | return *a + b 688 | } 689 | 690 | D. 691 | type Integer int 692 | func (a *Integer) Add(b *Integer) Integer { 693 | return *a + *b 694 | } 695 | ``` 696 | 697 | **答:A C** 698 | 699 | **解析:** 700 | 701 | 知识点:类型断言、方法集。 702 | 703 | ## 81. 关于 bool 变量 b 的赋值,下面错误的用法是? 704 | 705 | - A. b = true 706 | - B. b = 1 707 | - C. b = bool(1) 708 | - D. b = (1 == 2) 709 | 710 | **答:B C** 711 | 712 | ## 82. 关于变量的自增和自减操作,下面语句正确的是? 713 | 714 | ```go 715 | A. 716 | i := 1 717 | i++ 718 | 719 | B. 720 | i := 1 721 | j = i++ 722 | 723 | C. 724 | i := 1 725 | ++i 726 | 727 | D. 728 | i := 1 729 | i-- 730 | ``` 731 | 732 | **答:A D** 733 | 734 | **解析:** 735 | 736 | 知识点:自增自减操作。 737 | 738 | i++ 和 i-- 在 Go 语言中是语句,不是表达式,因此不能赋值给另外的变量。此外没有 ++i 和 --i。 739 | 740 | ## 83. 关于GetPodAction定义,下面赋值正确的是 741 | 742 | ```go 743 | type Fragment interface { 744 | Exec(transInfo *TransInfo) error 745 | } 746 | type GetPodAction struct { 747 | } 748 | func (g GetPodAction) Exec(transInfo *TransInfo) error { 749 | ... 750 | return nil 751 | } 752 | ``` 753 | 754 | - A. var fragment Fragment = new(GetPodAction) 755 | - B. var fragment Fragment = GetPodAction 756 | - C. var fragment Fragment = &GetPodAction{} 757 | - D. var fragment Fragment = GetPodAction{} 758 | 759 | **答:A C D** 760 | 761 | ## 84. 关于函数声明,下面语法正确的是? 762 | 763 | - A. func f(a, b int) (value int, err error) 764 | - B. func f(a int, b int) (value int, err error) 765 | - C. func f(a, b int) (value int, error) 766 | - D. func f(a int, b int) (int, int, error) 767 | 768 | **答:A B D** 769 | 770 | ## 85. 关于整型切片的初始化,下面正确的是? 771 | 772 | - A. s := make([]int) 773 | - B. s := make([]int, 0) 774 | - C. s := make([]int, 5, 10) 775 | - D. s := []int{1, 2, 3, 4, 5} 776 | 777 | **答:B C D** 778 | 779 | ## 86. 下面代码会触发异常吗?请说明。 780 | 781 | ```go 782 | func main() { 783 | runtime.GOMAXPROCS(1) 784 | int_chan := make(chan int, 1) 785 | string_chan := make(chan string, 1) 786 | int_chan <- 1 787 | string_chan <- "hello" 788 | select { 789 | case value := <-int_chan: 790 | fmt.Println(value) 791 | case value := <-string_chan: 792 | panic(value) 793 | } 794 | } 795 | ``` 796 | 797 | **解析:** 798 | 799 | `select` 会随机选择一个可用通道做收发操作,所以可能触发异常,也可能不会。 800 | 801 | ## 87. 关于channel的特性,下面说法正确的是? 802 | 803 | - A. 给一个 nil channel 发送数据,造成永远阻塞 804 | - B. 从一个 nil channel 接收数据,造成永远阻塞 805 | - C. 给一个已经关闭的 channel 发送数据,引起 panic 806 | - D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值 807 | 808 | **答:A B C D** 809 | 810 | ## 88. 下面代码有什么问题? 811 | 812 | ```go 813 | const i = 100 814 | var j = 123 815 | 816 | func main() { 817 | fmt.Println(&j, j) 818 | fmt.Println(&i, i) 819 | } 820 | ``` 821 | 822 | **答:编译报错** 823 | 824 | **解析:** 825 | 826 | 编译报错`cannot take the address of i`。知识点:常量。常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,所以常量无法寻址。 827 | 828 | ## 89. 下面代码能否编译通过?如果通过,输出什么? 829 | 830 | ```go 831 | func GetValue(m map[int]string, id int) (string, bool) { 832 | 833 | if _, exist := m[id]; exist { 834 | return "exist", true 835 | } 836 | return nil, false 837 | } 838 | func main() { 839 | intmap := map[int]string{ 840 | 1: "a", 841 | 2: "b", 842 | 3: "c", 843 | } 844 | 845 | v, err := GetValue(intmap, 3) 846 | fmt.Println(v, err) 847 | } 848 | ``` 849 | 850 | **答:不能通过编译** 851 | 852 | **解析:** 853 | 854 | 知识点:函数返回值类型。nil 可以用作 interface、function、pointer、map、slice 和 channel 的“空值”。但是如果不特别指定的话,Go 语言不能识别类型,所以会报错:`cannot use nil as type string in return argument` 855 | 856 | ## 90. 关于异常的触发,下面说法正确的是? 857 | 858 | - A. 空指针解析; 859 | - B. 下标越界; 860 | - C. 除数为0; 861 | - D. 调用panic函数; 862 | 863 | **答:A B C D** -------------------------------------------------------------------------------- /91-120.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [91\. 下面代码输出什么?](#91-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 4 | > * [92\. 下面这段代码能否编译通过?如果通过,输出什么?](#92-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%A6%82%E6%9E%9C%E9%80%9A%E8%BF%87%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 5 | > * [93\. 关于无缓冲和有冲突的channel,下面说法正确的是?](#93-%E5%85%B3%E4%BA%8E%E6%97%A0%E7%BC%93%E5%86%B2%E5%92%8C%E6%9C%89%E5%86%B2%E7%AA%81%E7%9A%84channel%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 6 | > * [94\. 下面代码是否能编译通过?如果通过,输出什么?](#94-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%98%AF%E5%90%A6%E8%83%BD%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%A6%82%E6%9E%9C%E9%80%9A%E8%BF%87%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 7 | > * [95\. 下面代码输出什么?](#95-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 8 | > * [96\. 关于select机制,下面说法正确的是?](#96-%E5%85%B3%E4%BA%8Eselect%E6%9C%BA%E5%88%B6%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 9 | > * [97\. 下面的代码有什么问题?](#97-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 10 | > * [98\. 下面这段代码存在什么问题?](#98-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E5%AD%98%E5%9C%A8%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 11 | > * [99\. 下面代码编译能通过吗?](#99-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E7%BC%96%E8%AF%91%E8%83%BD%E9%80%9A%E8%BF%87%E5%90%97) 12 | > * [100\. 下面这段代码输出什么?](#100-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 13 | > * [101\. 下面这段代码输出什么?](#101-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 14 | > * [102\. 请指出下面代码的错误?](#102-%E8%AF%B7%E6%8C%87%E5%87%BA%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E7%9A%84%E9%94%99%E8%AF%AF) 15 | > * [103\. 下面代码输出什么?](#103-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 16 | > * [104\. 下面代码输出什么?](#104-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 17 | > * [105\. 下面的代码有什么问题?](#105-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 18 | > * [106\. 下面代码输出什么?](#106-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 19 | > * [107\. 下面代码有几处错误的地方?请说明原因。](#107-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E5%87%A0%E5%A4%84%E9%94%99%E8%AF%AF%E7%9A%84%E5%9C%B0%E6%96%B9%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 20 | > * [108\. 下面代码有什么问题?](#108-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 21 | > * [109\. 下面的代码有什么问题?](#109-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 22 | > * [110\. 下面代码能编译通过吗?](#110-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%83%BD%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%90%97) 23 | > * [111\. 下面代码有什么错误?](#111-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%94%99%E8%AF%AF) 24 | > * [112\. 下面代码有什么问题?](#112-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 25 | > * [113\. 下面代码输出什么?](#113-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 26 | > * [114\. 下面的代码有什么问题?](#114-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 27 | > * [115\. 下面代码输出什么?](#115-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 28 | > * [116\. 下面代码有什么问题?](#116-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 29 | > * [117\. 下面的代码有什么问题?](#117-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 30 | > * [118\. 下面代码最后一行输出什么?请说明原因。](#118-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B7%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 31 | > * [119\. 下面代码有什么问题?](#119-%E4%B8%8B%E9%9D%A2%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98) 32 | > * [120\. 下面的代码输出什么?](#120-%E4%B8%8B%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 33 | > * [121\. 121-150题](https://github.com/yqchilde/Golang-Interview/blob/master/121-150.md) 34 | 35 | ## 91. 下面代码输出什么? 36 | 37 | ```go 38 | func main() { 39 | x := []string{"a", "b", "c"} 40 | for v := range x { 41 | fmt.Print(v) 42 | } 43 | } 44 | ``` 45 | 46 | **答:012** 47 | 48 | **解析:** 49 | 50 | 注意区别下面代码段: 51 | 52 | ```go 53 | func main() { 54 | x := []string{"a", "b", "c"} 55 | for _, v := range x { 56 | fmt.Print(v) //输出 abc 57 | } 58 | } 59 | ``` 60 | 61 | 62 | 63 | ## 92. 下面这段代码能否编译通过?如果通过,输出什么? 64 | 65 | ```go 66 | type User struct{} 67 | type User1 User 68 | type User2 = User 69 | 70 | func (i User) m1() { 71 | fmt.Println("m1") 72 | } 73 | func (i User) m2() { 74 | fmt.Println("m2") 75 | } 76 | 77 | func main() { 78 | var i1 User1 79 | var i2 User2 80 | i1.m1() 81 | i2.m2() 82 | } 83 | ``` 84 | 85 | **答:不能,报错`i1.m1 undefined (type User1 has no field or method m1)`** 86 | 87 | **解析:** 88 | 89 | 第 2 行代码基于类型 User 创建了新类型 User1,第 3 行代码是创建了 User 的类型别名 User2,注意使用 = 定义类型别名。因为 User2 是别名,完全等价于 User,所以 User2 具有 User 所有的方法。但是 i1.m1() 是不能执行的,因为 User1 没有定义该方法。 90 | 91 | ## 93. 关于无缓冲和有冲突的channel,下面说法正确的是? 92 | 93 | - A. 无缓冲的channel是默认的缓冲为1的channel; 94 | - B. 无缓冲的channel和有缓冲的channel都是同步的; 95 | - C. 无缓冲的channel和有缓冲的channel都是非同步的; 96 | - D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的; 97 | 98 | **答:D** 99 | 100 | ## 94. 下面代码是否能编译通过?如果通过,输出什么? 101 | 102 | ```go 103 | func Foo(x interface{}) { 104 | if x == nil { 105 | fmt.Println("empty interface") 106 | return 107 | } 108 | fmt.Println("non-empty interface") 109 | } 110 | func main() { 111 | var x *int = nil 112 | Foo(x) 113 | } 114 | ``` 115 | 116 | **答:non-empty interface** 117 | 118 | **解析:** 119 | 120 | 考点:interface 的内部结构,我们知道接口除了有静态类型,还有动态类型和动态值,当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。这里的 x 的动态类型是 `*int`,所以 x 不为 nil。 121 | 122 | ## 95. 下面代码输出什么? 123 | 124 | ```go 125 | func main() { 126 | ch := make(chan int, 100) 127 | // A 128 | go func() { 129 | for i := 0; i < 10; i++ { 130 | ch <- i 131 | } 132 | }() 133 | // B 134 | go func() { 135 | for { 136 | a, ok := <-ch 137 | if !ok { 138 | fmt.Println("close") 139 | return 140 | } 141 | fmt.Println("a: ", a) 142 | } 143 | }() 144 | close(ch) 145 | fmt.Println("ok") 146 | time.Sleep(time.Second * 10) 147 | } 148 | ``` 149 | 150 | **答:程序抛异常** 151 | 152 | **解析:** 153 | 154 | 先定义下,第一个协程为 A 协程,第二个协程为 B 协程;当 A 协程还没起时,主协程已经将 channel 关闭了,当 A 协程往关闭的 channel 发送数据时会 panic,`panic: send on closed channel`。 155 | 156 | ## 96. 关于select机制,下面说法正确的是? 157 | 158 | - A. select机制用来处理异步IO问题; 159 | - B. select机制最大的一条限制就是每个case语句里必须是一个IO操作; 160 | - C. golang在语言级别支持select关键字; 161 | - D. select关键字的用法与switch语句非常类似,后面要带判断条件; 162 | 163 | **答:A B C** 164 | 165 | ## 97. 下面的代码有什么问题? 166 | 167 | ```go 168 | func Stop(stop <-chan bool) { 169 | close(stop) 170 | } 171 | ``` 172 | 173 | **答:有方向的 channel 不可以被关闭。** 174 | 175 | ## 98. 下面这段代码存在什么问题? 176 | 177 | ```go 178 | type Param map[string]interface{} 179 | 180 | type Show struct { 181 | *Param 182 | } 183 | 184 | func main() { 185 | s := new(Show) 186 | s.Param["day"] = 2 187 | } 188 | ``` 189 | 190 | **答:存在两个问题** 191 | 192 | **解析:** 193 | 194 | 1. map 需要初始化才能使用; 195 | 196 | 2. 指针不支持索引。修复代码如下: 197 | 198 | ```go 199 | func main() { 200 | s := new(Show) 201 | // 修复代码 202 | p := make(Param) 203 | p["day"] = 2 204 | s.Param = &p 205 | tmp := *s.Param 206 | fmt.Println(tmp["day"]) 207 | } 208 | ``` 209 | 210 | ## 99. 下面代码编译能通过吗? 211 | 212 | ```go 213 | func main() 214 | { 215 | fmt.Println("hello world") 216 | } 217 | ``` 218 | 219 | **答:编译错误** 220 | 221 | ```shell 222 | syntax error: unexpected semicolon or newline before { 223 | ``` 224 | 225 | **解析:** 226 | 227 | Go 语言中,大括号不能放在单独的一行。 228 | 229 | 正确的代码如下: 230 | 231 | ```go 232 | func main() { 233 | fmt.Println("works") 234 | } 235 | ``` 236 | 237 | ## 100. 下面这段代码输出什么? 238 | 239 | ```go 240 | var x = []int{2: 2, 3, 0: 1} 241 | 242 | func main() { 243 | fmt.Println(x) 244 | } 245 | ``` 246 | 247 | **答:[1 0 2 3]** 248 | 249 | **解析:** 250 | 251 | 字面量初始化切片时候,可以指定索引,没有指定索引的元素会在前一个索引基础之上加一,所以输出`[1 0 2 3]`,而不是`[1 3 2]`。 252 | 253 | ## 101. 下面这段代码输出什么? 254 | 255 | ```go 256 | func incr(p *int) int { 257 | *p++ 258 | return *p 259 | } 260 | func main() { 261 | v := 1 262 | incr(&v) 263 | fmt.Println(v) 264 | } 265 | ``` 266 | 267 | **答:2** 268 | 269 | **解析:** 270 | 271 | 知识点:指针。 272 | 273 | p 是指针变量,指向变量 v,`*p++`操作的意思是取出变量 v 的值并执行加一操作,所以 v 的最终值是 2。 274 | 275 | ## 102. 请指出下面代码的错误? 276 | 277 | ```go 278 | package main 279 | 280 | var gvar int 281 | 282 | func main() { 283 | var one int 284 | two := 2 285 | var three int 286 | three = 3 287 | 288 | func(unused string) { 289 | fmt.Println("Unused arg. No compile error") 290 | }("what?") 291 | } 292 | ``` 293 | 294 | **答:变量 one、two 和 three 声明未使用** 295 | 296 | **解析:** 297 | 298 | 知识点:**未使用变量**。 299 | 300 | 如果有未使用的变量代码将编译失败。但也有例外,函数中声明的变量必须要使用,但可以有未使用的全局变量。函数的参数未使用也是可以的。 301 | 302 | 如果你给未使用的变量分配了一个新值,代码也还是会编译失败。你需要在某个地方使用这个变量,才能让编译器愉快的编译。 303 | 304 | 修复代码: 305 | 306 | ```go 307 | func main() { 308 | var one int 309 | _ = one 310 | 311 | two := 2 312 | fmt.Println(two) 313 | 314 | var three int 315 | three = 3 316 | one = three 317 | 318 | var four int 319 | four = four 320 | } 321 | ``` 322 | 323 | 另一个选择是注释掉或者移除未使用的变量 。 324 | 325 | ## 103. 下面代码输出什么? 326 | 327 | ```go 328 | type ConfigOne struct { 329 | Daemon string 330 | } 331 | 332 | func (c *ConfigOne) String() string { 333 | return fmt.Sprintf("print: %v", c) 334 | } 335 | 336 | func main() { 337 | c := &ConfigOne{} 338 | c.String() 339 | } 340 | ``` 341 | 342 | **答:运行时错误** 343 | 344 | **解析:** 345 | 346 | 如果类型实现 String() 方法,当格式化输出时会自动使用 String() 方法。上面这段代码是在该类型的 String() 方法内使用格式化输出,导致递归调用,最后抛错。 347 | 348 | ```shell 349 | runtime: goroutine stack exceeds 1000000000-byte limit 350 | fatal error: stack overflow 351 | ``` 352 | 353 | 354 | 355 | ## 104. 下面代码输出什么? 356 | 357 | ```go 358 | func main() { 359 | var a = []int{1, 2, 3, 4, 5} 360 | var r = make([]int, 0) 361 | 362 | for i, v := range a { 363 | if i == 0 { 364 | a = append(a, 6, 7) 365 | } 366 | 367 | r = append(r, v) 368 | } 369 | 370 | fmt.Println(r) 371 | } 372 | ``` 373 | 374 | **答:[1 2 3 4 5]** 375 | 376 | **解析:** 377 | 378 | a 在 for range 过程中增加了两个元素,len 由 5 增加到 7,但 for range 时会使用 a 的副本 a' 参与循环,副本的 len 依旧是 5,因此 for range 只会循环 5 次,也就只获取 a 对应的底层数组的前 5 个元素。 379 | 380 | ## 105. 下面的代码有什么问题? 381 | 382 | ```go 383 | import ( 384 | "fmt" 385 | "log" 386 | "time" 387 | ) 388 | func main() { 389 | } 390 | ``` 391 | 392 | **答:导入的包没有被使用** 393 | 394 | **解析:** 395 | 396 | 如果引入一个包,但是未使用其中如何函数、接口、结构体或变量的话,代码将编译失败。 397 | 398 | 如果你真的需要引入包,可以使用下划线操作符,`_`,来作为这个包的名字,从而避免失败。下划线操作符用于引入,但不使用。 399 | 400 | 我们还可以注释或者移除未使用的包。 401 | 402 | 修复代码: 403 | 404 | ```go 405 | import ( 406 | _ "fmt" 407 | "log" 408 | "time" 409 | ) 410 | var _ = log.Println 411 | func main() { 412 | _ = time.Now 413 | } 414 | ``` 415 | 416 | ## 106. 下面代码输出什么? 417 | 418 | ```go 419 | func main() { 420 | x := interface{}(nil) 421 | y := (*int)(nil) 422 | a := y == x 423 | b := y == nil 424 | _, c := x.(interface{}) 425 | println(a, b, c) 426 | } 427 | ``` 428 | 429 | - A. true true true 430 | - B. false true true 431 | - C. true true true 432 | - D. false true false 433 | 434 | **答:D** 435 | 436 | **解析:** 437 | 438 | 知识点:类型断言。 439 | 440 | 类型断言语法:i.(Type),其中 i 是接口,Type 是类型或接口。编译时会自动检测 i 的动态类型与 Type 是否一致。但是,如果动态类型不存在,则断言总是失败。 441 | 442 | ## 107. 下面代码有几处错误的地方?请说明原因。 443 | 444 | ```go 445 | func main() { 446 | var s []int 447 | s = append(s,1) 448 | 449 | var m map[string]int 450 | m["one"] = 1 451 | } 452 | ``` 453 | 454 | **答:有 1 处错误** 455 | 456 | **解析:** 457 | 458 | 有 1 处错误,不能对 nil 的 map 直接赋值,需要使用 make() 初始化。但可以使用 append() 函数对为 nil 的 slice 增加元素。 459 | 460 | 修复代码: 461 | 462 | ```go 463 | func main() { 464 | var m map[string]int 465 | m = make(map[string]int) 466 | m["one"] = 1 467 | } 468 | ``` 469 | 470 | ## 108. 下面代码有什么问题? 471 | 472 | ```go 473 | func main() { 474 | m := make(map[string]int,2) 475 | cap(m) 476 | } 477 | ``` 478 | 479 | **答:使用 cap() 获取 map 的容量** 480 | 481 | **解析:** 482 | 483 | 1. 使用 make 创建 map 变量时可以指定第二个参数,不过会被忽略。 484 | 485 | 2. cap() 函数适用于数组、数组指针、slice 和 channel,不适用于 map,可以使用 len() 返回 map 的元素个数。 486 | 487 | ## 109. 下面的代码有什么问题? 488 | 489 | ```go 490 | func main() { 491 | var x = nil 492 | _ = x 493 | } 494 | ``` 495 | 496 | **解析:** 497 | 498 | nil 用于表示 interface、函数、maps、slices 和 channels 的“零值”。如果不指定变量的类型,编译器猜不出变量的具体类型,导致编译错误。 499 | 500 | 修复代码: 501 | 502 | ```go 503 | func main() { 504 | var x interface{} = nil 505 | _ = x 506 | } 507 | ``` 508 | 509 | ## 110. 下面代码能编译通过吗? 510 | 511 | ```go 512 | type info struct { 513 | result int 514 | } 515 | 516 | func work() (int,error) { 517 | return 13,nil 518 | } 519 | 520 | func main() { 521 | var data info 522 | 523 | data.result, err := work() 524 | fmt.Printf("info: %+v\n",data) 525 | } 526 | ``` 527 | 528 | **答:编译失败** 529 | 530 | ```shell 531 | non-name data.result on left side of := 532 | ``` 533 | 534 | **解析:** 535 | 536 | 不能使用短变量声明设置结构体字段值,修复代码: 537 | 538 | ```go 539 | func main() { 540 | var data info 541 | 542 | var err error 543 | data.result, err = work() //ok 544 | if err != nil { 545 | fmt.Println(err) 546 | return 547 | } 548 | 549 | fmt.Println(data) 550 | } 551 | ``` 552 | 553 | ## 111. 下面代码有什么错误? 554 | 555 | ```go 556 | func main() { 557 | one := 0 558 | one := 1 559 | } 560 | ``` 561 | 562 | **答:变量重复声明** 563 | 564 | **解析:** 565 | 566 | 不能在单独的声明中重复声明一个变量,但在多变量声明的时候是可以的,但必须保证至少有一个变量是新声明的。 567 | 568 | 修复代码: 569 | 570 | ```go 571 | func main() { 572 | one := 0 573 | one, two := 1,2 574 | one,two = two,one 575 | } 576 | ``` 577 | 578 | ## 112. 下面代码有什么问题? 579 | 580 | ```go 581 | func main() { 582 | x := []int{ 583 | 1, 584 | 2 585 | } 586 | _ = x 587 | } 588 | ``` 589 | 590 | **答:编译错误** 591 | 592 | **解析:** 593 | 594 | 第四行代码没有逗号。用字面量初始化数组、slice 和 map 时,最好是在每个元素后面加上逗号,即使是声明在一行或者多行都不会出错。 595 | 596 | 修复代码: 597 | 598 | ```go 599 | func main() { 600 | x := []int{ // 多行 601 | 1, 602 | 2, 603 | } 604 | x = x 605 | 606 | y := []int{3,4,} // 一行 no error 607 | y = y 608 | } 609 | ``` 610 | 611 | 612 | 613 | ## 113. 下面代码输出什么? 614 | 615 | ```go 616 | func test(x byte) { 617 | fmt.Println(x) 618 | } 619 | 620 | func main() { 621 | var a byte = 0x11 622 | var b uint8 = a 623 | var c uint8 = a + b 624 | test(c) 625 | } 626 | ``` 627 | 628 | **答:34** 629 | 630 | **解析:** 631 | 632 | 与 rune 是 int32 的别名一样,byte 是 uint8 的别名,别名类型无序转换,可直接转换。 633 | 634 | ## 114. 下面的代码有什么问题? 635 | 636 | ```go 637 | func main() { 638 | const x = 123 639 | const y = 1.23 640 | fmt.Println(x) 641 | } 642 | ``` 643 | 644 | **答:编译可以通过** 645 | 646 | **解析:** 647 | 648 | 知识点:常量。 649 | 650 | 常量是一个简单值的标识符,在程序运行时,不会被修改的量。不像变量,常量未使用是能编译通过的。 651 | 652 | ## 115. 下面代码输出什么? 653 | 654 | ```go 655 | const ( 656 | x uint16 = 120 657 | y 658 | s = "abc" 659 | z 660 | ) 661 | 662 | func main() { 663 | fmt.Printf("%T %v\n", y, y) 664 | fmt.Printf("%T %v\n", z, z) 665 | } 666 | ``` 667 | 668 | **答:** 669 | 670 | ```shell 671 | uint16 120 672 | string abc 673 | ``` 674 | 675 | **解析:** 676 | 677 | 常量组中如不指定类型和初始化值,则与上一行非空常量右值相同 678 | 679 | ## 116. 下面代码有什么问题? 680 | 681 | ```go 682 | func main() { 683 | var x string = nil 684 | 685 | if x == nil { 686 | x = "default" 687 | } 688 | } 689 | ``` 690 | 691 | **答:将 nil 分配给 string 类型的变量** 692 | 693 | **解析:** 694 | 695 | 修复代码: 696 | 697 | ```go 698 | func main() { 699 | var x string //defaults to "" (zero value) 700 | 701 | if x == "" { 702 | x = "default" 703 | } 704 | } 705 | ``` 706 | 707 | ## 117. 下面的代码有什么问题? 708 | 709 | ```go 710 | func main() { 711 | data := []int{1,2,3} 712 | i := 0 713 | ++i 714 | fmt.Println(data[i++]) 715 | } 716 | ``` 717 | 718 | **解析:** 719 | 720 | 对于自增、自减,需要注意: 721 | 722 | - 自增、自减不在是运算符,只能作为独立语句,而不是表达式; 723 | - 不像其他语言,Go 语言中不支持 ++i 和 --i 操作; 724 | 725 | 表达式通常是求值代码,可作为右值或参数使用。而语句表示完成一个任务,比如 if、for 语句等。表达式可作为语句使用,但语句不能当做表达式。 726 | 727 | 修复代码: 728 | 729 | ```go 730 | func main() { 731 | data := []int{1,2,3} 732 | i := 0 733 | i++ 734 | fmt.Println(data[i]) 735 | } 736 | ``` 737 | 738 | ## 118. 下面代码最后一行输出什么?请说明原因。 739 | 740 | ```go 741 | func main() { 742 | x := 1 743 | fmt.Println(x) 744 | { 745 | fmt.Println(x) 746 | i,x := 2,2 747 | fmt.Println(i,x) 748 | } 749 | fmt.Println(x) // print ? 750 | } 751 | ``` 752 | 753 | **答:输出`1`** 754 | 755 | **解析:** 756 | 757 | 知识点:变量隐藏。 758 | 759 | 使用变量简短声明符号 := 时,如果符号左边有多个变量,只需要保证至少有一个变量是新声明的,并对已定义的变量尽进行赋值操作。但如果出现作用域之后,就会导致变量隐藏的问题,就像这个例子一样。 760 | 761 | 这个坑很容易挖,但又很难发现。即使对于经验丰富的 Go 开发者而言,这也是一个非常常见的陷阱。 762 | 763 | ## 119. 下面代码有什么问题? 764 | 765 | ```go 766 | type foo struct { 767 | bar int 768 | } 769 | 770 | func main() { 771 | var f foo 772 | f.bar, tmp := 1, 2 773 | } 774 | ``` 775 | 776 | **答:编译错误** 777 | 778 | ```shell 779 | non-name f.bar on left side of := 780 | ``` 781 | 782 | **解析:** 783 | 784 | `:=` 操作符不能用于结构体字段赋值。 785 | 786 | ## 120. 下面的代码输出什么? 787 | 788 | ```go 789 | func main() { 790 | fmt.Println(~2) 791 | } 792 | ``` 793 | 794 | **答:编译错误** 795 | 796 | ```shell 797 | invalid character U+007E '~' 798 | ``` 799 | 800 | **解析:** 801 | 802 | 很多语言都是采用 `~` 作为按位取反运算符,Go 里面采用的是` ^` 。按位取反之后返回一个每个 bit 位都取反的数,对于有符号的整数来说,是按照补码进行取反操作的(快速计算方法:对数 a 取反,结果为 -(a+1) ),对于无符号整数来说就是按位取反。例如: 803 | 804 | ```go 805 | func main() { 806 | var a int8 = 3 807 | var b uint8 = 3 808 | var c int8 = -3 809 | 810 | fmt.Printf("^%b=%b %d\n", a, ^a, ^a) // ^11=-100 -4 811 | fmt.Printf("^%b=%b %d\n", b, ^b, ^b) // ^11=11111100 252 812 | fmt.Printf("^%b=%b %d\n", c, ^c, ^c) // ^-11=10 2 813 | } 814 | ``` 815 | 816 | 另外需要注意的是,如果作为二元运算符,^ 表示按位异或,即:对应位相同为 0,相异为 1。例如: 817 | 818 | ```go 819 | func main() { 820 | var a int8 = 3 821 | var c int8 = 5 822 | 823 | fmt.Printf("a: %08b\n",a) 824 | fmt.Printf("c: %08b\n",c) 825 | fmt.Printf("a^c: %08b\n",a ^ c) 826 | } 827 | ``` 828 | 829 | 给大家重点介绍下这个操作符 &^,按位置零,例如:z = x &^ y,表示如果 y 中的 bit 位为 1,则 z 对应 bit 位为 0,否则 z 对应 bit 位等于 x 中相应的 bit 位的值。 830 | 831 | 不知道大家发现没有,我们还可以这样理解或操作符 | ,表达式 z = x | y,如果 y 中的 bit 位为 1,则 z 对应 bit 位为 1,否则 z 对应 bit 位等于 x 中相应的 bit 位的值,与 &^ 完全相反。 832 | 833 | ```go 834 | var x uint8 = 214 835 | var y uint8 = 92 836 | fmt.Printf("x: %08b\n",x) 837 | fmt.Printf("y: %08b\n",y) 838 | fmt.Printf("x | y: %08b\n",x | y) 839 | fmt.Printf("x &^ y: %08b\n",x &^ y) 840 | ``` 841 | 842 | 输出: 843 | 844 | ```shell 845 | x: 11010110 846 | y: 01011100 847 | x | y: 11011110 848 | x &^ y: 10000010 849 | ``` 850 | 851 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 目录 2 | > 3 | > * [1\. 下面这段代码输出的内容:](#1-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E7%9A%84%E5%86%85%E5%AE%B9) 4 | > * [2\. 下面这段代码输出什么,说明原因。](#2-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E8%AF%B4%E6%98%8E%E5%8E%9F%E5%9B%A0) 5 | > * [3\. 下面两段代码输出什么?](#3-%E4%B8%8B%E9%9D%A2%E4%B8%A4%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 6 | > * [4\. 下面这段代码有什么缺陷?](#4-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E6%9C%89%E4%BB%80%E4%B9%88%E7%BC%BA%E9%99%B7) 7 | > * [5\. new() 与 make() 的区别](#5-new-%E4%B8%8E-make-%E7%9A%84%E5%8C%BA%E5%88%AB) 8 | > * [6\. 下面这段代码能否通过编译,不能的话原因是什么;如果能,输出什么?](#6-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E9%80%9A%E8%BF%87%E7%BC%96%E8%AF%91%E4%B8%8D%E8%83%BD%E7%9A%84%E8%AF%9D%E5%8E%9F%E5%9B%A0%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E8%83%BD%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 9 | > * [7\. 下面这段代码能否通过编译,不能的话原因是什么;如果可以,输出什么?](#7%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E9%80%9A%E8%BF%87%E7%BC%96%E8%AF%91%E4%B8%8D%E8%83%BD%E7%9A%84%E8%AF%9D%E5%8E%9F%E5%9B%A0%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 10 | > * [8\. 下面这段代码能否通过编译,如果可以,输出什么?](#8-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E9%80%9A%E8%BF%87%E7%BC%96%E8%AF%91%E5%A6%82%E6%9E%9C%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 11 | > * [9\. 下面这段代码能否通过编译?不能的话,原因是什么?如果通过,输出什么?](#9%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E9%80%9A%E8%BF%87%E7%BC%96%E8%AF%91%E4%B8%8D%E8%83%BD%E7%9A%84%E8%AF%9D%E5%8E%9F%E5%9B%A0%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E9%80%9A%E8%BF%87%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 12 | > * [10\. 通过指针变量p访问其成员变量name,有哪几种方式?](#10-%E9%80%9A%E8%BF%87%E6%8C%87%E9%92%88%E5%8F%98%E9%87%8Fp%E8%AE%BF%E9%97%AE%E5%85%B6%E6%88%90%E5%91%98%E5%8F%98%E9%87%8Fname%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F) 13 | > * [11\. 下面这段代码能否通过编译?如果通过,输出什么?](#11-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E9%80%9A%E8%BF%87%E7%BC%96%E8%AF%91%E5%A6%82%E6%9E%9C%E9%80%9A%E8%BF%87%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 14 | > * [12\. 以下代码输出什么?](#12-%E4%BB%A5%E4%B8%8B%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 15 | > * [13\. 关于字符串连接,下面语法正确的是?](#13-%E5%85%B3%E4%BA%8E%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%BF%9E%E6%8E%A5%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 16 | > * [14\. 下面这段代码能否编译通过?如果可以,输出什么?](#14-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%A6%82%E6%9E%9C%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 17 | > * [15\. 下面赋值正确的是()](#15-%E4%B8%8B%E9%9D%A2%E8%B5%8B%E5%80%BC%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 18 | > * [16\. 关于init函数,下面说法正确的是()](#16-%E5%85%B3%E4%BA%8Einit%E5%87%BD%E6%95%B0%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 19 | > * [17\. 下面这段代码输出什么以及原因?](#17-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88%E4%BB%A5%E5%8F%8A%E5%8E%9F%E5%9B%A0) 20 | > * [18\. 下面这段代码能否编译通过?如果可以,输出什么?](#18-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%83%BD%E5%90%A6%E7%BC%96%E8%AF%91%E9%80%9A%E8%BF%87%E5%A6%82%E6%9E%9C%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 21 | > * [19\. 关于channel,下面语法正确的是()](#19-%E5%85%B3%E4%BA%8Echannel%E4%B8%8B%E9%9D%A2%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 22 | > * [20\. 下面这段代码输出什么?](#20-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 23 | > * [21\. 下面这段代码输出什么?](#21-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 24 | > * [22\. 下面这段代码输出什么?](#22-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 25 | > * [23\. 下面这段代码输出什么?](#23-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 26 | > * [24\. 下面这段代码输出什么?](#24-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 27 | > * [25\. 关于 cap() 函数的适用类型,下面说法正确的是()](#25-%E5%85%B3%E4%BA%8E-cap-%E5%87%BD%E6%95%B0%E7%9A%84%E9%80%82%E7%94%A8%E7%B1%BB%E5%9E%8B%E4%B8%8B%E9%9D%A2%E8%AF%B4%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%98%AF) 28 | > * [26\. 下面这段代码输出什么?](#26-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 29 | > * [27\. 下面这段代码输出什么?](#27-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 30 | > * [28\. 下面属于关键字的是()](#28-%E4%B8%8B%E9%9D%A2%E5%B1%9E%E4%BA%8E%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E6%98%AF) 31 | > * [29\. 下面这段代码输出什么?](#29-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 32 | > * [30\. 下面这段代码输出什么?](#30-%E4%B8%8B%E9%9D%A2%E8%BF%99%E6%AE%B5%E4%BB%A3%E7%A0%81%E8%BE%93%E5%87%BA%E4%BB%80%E4%B9%88) 33 | > * [31\. 31-60题](https://github.com/yqchilde/Golang-Interview/blob/master/31-60.md) 34 | 35 | ## 1. 下面这段代码输出的内容: 36 | 37 | ```go 38 | package main 39 | 40 | import "fmt" 41 | 42 | func main() { 43 | defer_call() 44 | } 45 | 46 | func defer_call() { 47 | defer func() {fmt.Println("打印前")}() 48 | defer func() {fmt.Println("打印中")}() 49 | defer func() {fmt.Println("打印后")}() 50 | 51 | panic("触发异常") 52 | } 53 | ``` 54 | 55 | **答:输出内容为:** 56 | 57 | ```shell 58 | 打印后 59 | 打印中 60 | 打印前 61 | panic: 触发异常 62 | ``` 63 | 64 | **解析:** 65 | `defer` 的执行顺序是先进后出。出现panic语句的时候,会先按照 `defer` 的后进先出顺序执行,最后才会执行panic。 66 | 67 | ## 2. 下面这段代码输出什么,说明原因。 68 | 69 | ```go 70 | package main 71 | 72 | import "fmt" 73 | 74 | func main() { 75 | slice := []int{0, 1, 2, 3} 76 | m := make(map[int]*int) 77 | 78 | for key, val := range slice { 79 | m[key] = &val 80 | } 81 | 82 | for k, v := range m { 83 | fmt.Println(k, "->", *v) 84 | } 85 | } 86 | ``` 87 | 88 | **答:输出内容为:** 89 | 90 | ```shell 91 | // 注:key的顺序无法确定 92 | 0 -> 3 93 | 1 -> 3 94 | 2 -> 3 95 | 3 -> 3 96 | ``` 97 | 98 | **解析:** 99 | 100 | `for range` 循环的时候会创建每个元素的副本,而不是每个元素的引用,所以 `m[key] = &val` 取的都是变量val的地址,所以最后 `map` 中的所有元素的值都是变量 `val` 的地址,因为最后 `val` 被赋值为3,所有输出的都是3。 101 | 102 | ## 3. 下面两段代码输出什么? 103 | 104 | ```go 105 | // 1. 106 | func main() { 107 | s := make([]int, 5) 108 | s = append(s, 1, 2, 3) 109 | fmt.Println(s) 110 | } 111 | 112 | // 2. 113 | func main() { 114 | s := make([]int, 0) 115 | s = append(s, 1, 2, 3, 4) 116 | fmt.Println(s) 117 | } 118 | ``` 119 | 120 | **答:输出内容为:** 121 | 122 | ```shell 123 | // 1. 124 | [0 0 0 0 0 1 2 3] 125 | 126 | // 2. 127 | [1 2 3 4] 128 | ``` 129 | 130 | **解析:** 131 | 132 | 使用 `append` 向 `slice` 中添加元素,第一题中slice容量为5,所以补5个0,第二题为0,所以不需要。 133 | 134 | ## 4. 下面这段代码有什么缺陷? 135 | 136 | ```go 137 | func funcMui(x, y int) (sum int, error) { 138 | return x + y, nil 139 | } 140 | ``` 141 | 142 | **答:第二个返回值没有命名** 143 | 144 | **解析:** 145 | 146 | 在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。如果有多个返回值必须加上括号();如果只有一个返回值且命名也需要加上括号()。这里的第一个返回值有命名sum,第二个没有命名,所以错误。 147 | 148 | ## 5. new() 与 make() 的区别 149 | 150 | **解析:** 151 | 152 | - `new(T)`  和 `make(T, args)`  是Go语言内建函数,用来分配内存,但适用的类型不用。 153 | - `new(T)` 会为了 `T` 类型的新值分配已置零的内存空间,并返回地址(指针),即类型为 `*T` 的值。换句话说就是,返回一个指针,该指针指向新分配的、类型为 `T` 的零值。适用于值类型,如 `数组` 、 `结构体` 等。 154 | - `make(T, args)` 返回初始化之后的T类型的值,也不是指针 `*T` ,是经过初始化之后的T的引用。 `make()` 只适用于 `slice` 、 `map` 和 `channel` 。 155 | 156 | ## 6. 下面这段代码能否通过编译,不能的话原因是什么;如果能,输出什么? 157 | 158 | ```go 159 | func main() { 160 | list := new([]int) 161 | list = append(list, 1) 162 | fmt.Println(list) 163 | } 164 | ``` 165 | 166 | **答:不能通过** 167 | 168 | **解析:** 169 | 170 | 不能通过编译, `new([]int)` 之后的 `list` 是一个 `*int[]` 类型的指针,不能对指针执行 `append` 操作。可以使用 `make()` 初始化之后再用。同样的, `map` 和 `channel` 建议使用 `make()` 或字面量的方式初始化,不要用 `new` 。 171 | 172 | ## 7. 下面这段代码能否通过编译,不能的话原因是什么;如果可以,输出什么? 173 | 174 | ```go 175 | func main() { 176 | s1 := []int{1, 2, 3} 177 | s2 := []int{4, 5} 178 | s1 = append(s1, s2) 179 | fmt.Println(s1) 180 | } 181 | ``` 182 | 183 | **答:不能通过** 184 | 185 | **解析:** 186 | 187 | `append()` 的第二个参数不能直接使用 `slice` ,需使用 `...` 操作符,将一个切片追加到另一个切片上: `append(s1, s2...)` 。或者直接跟上元素,形如: `append(s1, 1, 2, 3)`  。 188 | 189 | ## 8. 下面这段代码能否通过编译,如果可以,输出什么? 190 | 191 | ```go 192 | var ( 193 | size := 1024 194 | max_size = size * 2 195 | ) 196 | 197 | func main() { 198 | fmt.Println(size, max_size) 199 | } 200 | ``` 201 | 202 | **答:不能通过** 203 | 204 | **解析:** 205 | 206 | 这道题的主要知识点是变量的简短模式,形如:x := 100 。但这种声明方式有限制: 207 | 208 | 1. 必须使用显示初始化; 209 | 1. 不能提供数据类型,编译器会自动推导; 210 | 1. 只能在函数内部使用简短模式; 211 | 212 | ## 9. 下面这段代码能否通过编译?不能的话,原因是什么?如果通过,输出什么? 213 | 214 | ```go 215 | func main() { 216 | sn1 := struct { 217 | age int 218 | name string 219 | }{age: 11, name: "qq"} 220 | sn2 := struct { 221 | age int 222 | name string 223 | }{age: 11, name: "11"} 224 | 225 | if sn1 == sn2 { 226 | fmt.Println("sn1 == sn2") 227 | } 228 | 229 | sm1 := struct { 230 | age int 231 | m map[string]string 232 | }{age: 11, m: map[string]string{"a": "1"}} 233 | sm2 := struct { 234 | age int 235 | m map[string]string 236 | }{age: 11, m: map[string]string{"a": "1"}} 237 | 238 | if sm1 == sm2 { 239 | fmt.Println("sm1 == sm2") 240 | } 241 | } 242 | ``` 243 | 244 | **答:不能通过,invalid operation: sm1 == sm2** 245 | 246 | **解析:** 247 | 248 | 考点是结构体的比较,有几个需要注意的地方: 249 | 250 | 1. 结构体只能比较是否相等,但是不能比较大小; 251 | 1. 想同类型的结构体才能进行比较,结构体是否相同不但与属性类型有关,还与属性顺序相关; 252 | 1. 如果struct的所有成员都可以比较,则该struct就可以通过==或!=进行比较是否相同,比较时逐个项进行比较,如果每一项都相等,则两个结构体才相等,否则不相等; 253 | 254 | **那有什么是可以比较的呢?** 255 | 256 | - 常见的有bool、数值型、字符、指针、数组等 257 | 258 | **不能比较的有** 259 | 260 | - slice、map、函数 261 | 262 | ## 10. 通过指针变量p访问其成员变量name,有哪几种方式? 263 | 264 | - A. p.name 265 | - B. (&p).name 266 | - C. (*p).name 267 | - D. p->name 268 | 269 | **答:A C** 270 | 271 | **解析:** 272 | 273 | `&` 取址运算符, `*` 指针解引用 274 | 275 | ## 11. 下面这段代码能否通过编译?如果通过,输出什么? 276 | 277 | ```go 278 | package main 279 | 280 | import "fmt" 281 | 282 | type MyInt1 int 283 | type MyInt2 = int 284 | 285 | func main() { 286 | var i int = 0 287 | var i1 MyInt1 = i 288 | var i2 MyInt2 = i 289 | fmt.Println(i1, i2) 290 | } 291 | ``` 292 | 293 | **答:不能通过** 294 | 295 | **解析:** 296 | 297 | 这道题考的是 `类型别名` 与 `类型定义` 的区别 298 | 第5行代码是基于类型 `int` 创建了新类型 `MyInt1` ,第6行代码是创建了int的类型别名 `MyInt2` ,注意类型别名的定义是 `=` 。所以,第10行代码相当于是将int类型的变量赋值给MyInt1类型的变量,Go是强类型语言,编译当然不通过;而MyInt2只是int的别名,本质上还是int,可以赋值。 299 | 第10行代码的赋值可以使用强制类型转换 `var i1 MyInt1 = MyInt1(i)`  300 | 301 | ## 12. 以下代码输出什么? 302 | 303 | ```go 304 | func main() { 305 | a := []int{7, 8, 9} 306 | fmt.Printf("%+v\n", a) 307 | ap(a) 308 | fmt.Printf("%+v\n", a) 309 | app(a) 310 | fmt.Printf("%+v\n", a) 311 | } 312 | 313 | func ap(a []int) { 314 | a = append(a, 10) 315 | } 316 | 317 | func app(a []int) { 318 | a[0] = 1 319 | } 320 | ``` 321 | 322 | **答:输出内容为:** 323 | 324 | ```shell 325 | [7 8 9] 326 | [7 8 9] 327 | [1 8 9] 328 | ``` 329 | 330 | **解析:** 331 | 332 | 因为append导致底层数组重新分配内存了,append中的a这个alice的底层数组和外面不是一个,并没有改变外面的。 333 | 334 | ## 13. 关于字符串连接,下面语法正确的是? 335 | 336 | - A. str := 'abc' + '123' 337 | - B. str := "abc" + "123" 338 | - C. str := '123' + "abc" 339 | - D. fmt.Sprintf("abc%d", 123) 340 | 341 | **答:B、D** 342 | 343 | **解析:** 344 | 345 | 在Golang中字符串用双引号,字符用单引号 346 | 字符串连接除了以上两种连接方式,还有 `strings.Join()` 、 `buffer.WriteString()` 等 347 | 348 | ## 14. 下面这段代码能否编译通过?如果可以,输出什么? 349 | 350 | ```go 351 | const ( 352 | x = iota 353 | _ 354 | y 355 | z = "zz" 356 | k 357 | p = iota 358 | ) 359 | 360 | func main() { 361 | fmt.Println(x, y, z, k, p) 362 | } 363 | ``` 364 | 365 | **答:编译通过,输出:**`**0 2 zz zz 5**`  366 | 367 | **解析:** 368 | 369 | iota初始值为0,所以x为0,_表示不赋值,但是iota是从上往下加1的,所以y是2,z是“zz”,k和上面一个同值也是“zz”,p是iota,从上0开始数他是5 370 | 371 | ## 15. 下面赋值正确的是() 372 | 373 | - A. var x = nil 374 | - B. var x interface{} = nil 375 | - C. var x string = nil 376 | - D. var x error = nil 377 | 378 | **答:B、D** 379 | 380 | **解析:** 381 | 382 | A错在没有写类型,C错在字符串的空值是 `""` 而不是nil。 383 | 知识点:nil只能赋值给指针、chan、func、interface、map、或slice、类型的变量。 384 | 385 | ## 16. 关于init函数,下面说法正确的是() 386 | 387 | - A. 一个包中,可以包含多个init函数; 388 | - B. 程序编译时,先执行依赖包的init函数,再执行main包内的init函数; 389 | - C. main包中,不能有init函数; 390 | - D. init函数可以被其他函数调用; 391 | 392 | **答:A、B** 393 | 394 | **解析:** 395 | 396 | 1. init()函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等; 397 | 1. 一个包可以出现多个init()函数,一个源文件也可以包含多个init()函数; 398 | 1. 同一个包中多个init()函数的执行顺序没有明确的定义,但是不同包的init函数是根据包导入的依赖关系决定的; 399 | 1. init函数在代码中不能被显示调用、不能被引用(赋值给函数变量),否则出现编译失败; 400 | 1. 一个包被引用多次,如A import B,C import B,A import C,B被引用多次,但B包只会初始化一次; 401 | 1. 引入包,不可出现死循环。即A import B,B import A,这种情况下编译失败; 402 | 403 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/517869/1574040068413-cfcc17c2-6f8b-4c1c-b09b-d6d297c9f745.png#align=left&display=inline&height=330&name=image.png&originHeight=419&originWidth=948&size=156149&status=done&width=746) 404 | 405 | 406 | ## 17. 下面这段代码输出什么以及原因? 407 | 408 | ```go 409 | func hello() []string { 410 | return nil 411 | } 412 | 413 | func main() { 414 | h := hello 415 | if h == nil { 416 | fmt.Println("nil") 417 | } else { 418 | fmt.Println("not nil") 419 | } 420 | } 421 | ``` 422 | 423 | - A. nil 424 | - B. not nil 425 | - C. compilation error 426 | 427 | **答:B** 428 | 429 | **解析:** 430 | 431 | 这道题里面,是将 `hello()` 赋值给变量h,而不是函数的返回值,所以输出 `not nil`  432 | 433 | ## 18. 下面这段代码能否编译通过?如果可以,输出什么? 434 | 435 | ```go 436 | func GetValue() int { 437 | return 1 438 | } 439 | 440 | func main() { 441 | i := GetValue() 442 | switch i.(type) { 443 | case int: 444 | fmt.Println("int") 445 | case string: 446 | fmt.Println("string") 447 | case interface{}: 448 | fmt.Println("interface") 449 | default: 450 | fmt.Println("unknown") 451 | } 452 | } 453 | ``` 454 | 455 | **答:编译失败** 456 | 457 | **解析:** 458 | 459 | 只有接口类型才能使用类型选择 460 | 类型选择的语法形如:i.(type),其中i是接口,type是固定关键字,需要注意的是,只有接口类型才可以使用类型选择。 461 | 462 | ## 19. 关于channel,下面语法正确的是() 463 | 464 | - A. var ch chan int 465 | - B. ch := make(chan int) 466 | - C. <-ch 467 | - D. ch<- 468 | 469 | **答:A、B、C** 470 | 471 | **解析:** 472 | 473 | A、B都是申明channel;C读取channel;写channel是必须带上值,所以D错误。 474 | 475 | ## 20. 下面这段代码输出什么? 476 | 477 | - A. 0 478 | - B. 1 479 | - C. Compilation error 480 | 481 | ```go 482 | type person struct { 483 | name string 484 | } 485 | 486 | func main() { 487 | var m map[person]int 488 | p := person{"make"} 489 | fmt.Println(m[p]) 490 | } 491 | ``` 492 | 493 | **答:A** 494 | 495 | **解析:** 496 | 497 | 打印一个map中不存在的值时,返回元素类型的零值。这个例子中,m的类型是map[person]int,因为m中 不存在p,所以打印int类型的零值,即0。 498 | 499 | ## 21. 下面这段代码输出什么? 500 | 501 | - A. 18 502 | - B. 5 503 | - C. Compilation error 504 | 505 | ```go 506 | func hello(num ...int) { 507 | num[0] = 18 508 | } 509 | 510 | func main() { 511 | i := []int{5, 6, 7} 512 | hello(i...) 513 | fmt.Println(i[0]) 514 | } 515 | ``` 516 | 517 | **答:18** 518 | 519 | **解析:** 520 | 521 | 可变参数传递过去,改变了第一个值。 522 | 523 | ## 22. 下面这段代码输出什么? 524 | 525 | ```go 526 | func main() { 527 | a := 5 528 | b := 8.1 529 | fmt.Println(a + b) 530 | } 531 | ``` 532 | 533 | - A. 13.1 534 | - B. 13 535 | - C. compilation error 536 | 537 | **答:C** 538 | 539 | **解析:** 540 | 541 | `a` 的类型是`int` ,`b` 的类型是`float` ,两个不同类型的数值不能相加,编译报错。 542 | 543 | ## 23. 下面这段代码输出什么? 544 | 545 | ```go 546 | package main 547 | 548 | import ( 549 | "fmt" 550 | ) 551 | 552 | func main() { 553 | a := [5]int{1, 2, 3, 4, 5} 554 | t := a[3:4:4] 555 | fmt.Println(t[0]) 556 | } 557 | ``` 558 | 559 | - A. 3 560 | - B. 4 561 | - C. compilation error 562 | 563 | **答:B** 564 | 565 | **解析:** 566 | 567 | - 知识点:操作符 `[i, j]`。基于数组(切片)可以使用操作符 `[i, j]`创建新的切片,从索引 `i` ,到索引 `i` ,到索引 `j` 结束,截取已有数组(切片)的任意部分,返回新的切片,新切片的值包含原数组(切片)的 `i` 索引的值,但是不包含 `j` 索引的值。`i` 、`j` 都是可选的,`i` 如果省略,默认是0,`j` 如果省略,默认是原数组(切片)的长度。`i` 、`j` 都不能超过这个长度值。 568 | 569 | - 假如底层数组的大小为 k,截取之后获得的切片的长度和容量的计算方法:**长度:j-i,容量:k-i**。 570 | 571 | 截取操作符还可以有第三个参数,形如 [i,j,k],第三个参数 k 用来限制新切片的容量,但不能超过原数组(切片)的底层数组大小。截取获得的切片的长度和容量分别是:**j-i、k-i**。 572 | 573 | 所以例子中,切片 t 为 [4],长度和容量都是 1。 574 | 575 | ## 24. 下面这段代码输出什么? 576 | 577 | ```go 578 | func main() { 579 | a := [2]int{5, 6} 580 | b := [3]int{5, 6} 581 | if a == b { 582 | fmt.Println("equal") 583 | } else { 584 | fmt.Println("not equal") 585 | } 586 | } 587 | ``` 588 | 589 | - A. compilation error 590 | - B. equal 591 | - C. not equal 592 | 593 | **答:A** 594 | 595 | **解析:** 596 | 597 | Go中的数组是值类型,可比较,另外一方面,数组的长度也是数组类型的组成部分,所以 `a` 和 `b` 是不同的类型,是不能比较的,所以编译错误。 598 | 599 | ## 25. 关于 cap() 函数的适用类型,下面说法正确的是() 600 | 601 | - A. array 602 | - B. slice 603 | - C. map 604 | - D. channel 605 | 606 | **答:A、B、D** 607 | 608 | **解析:** 609 | 610 | cap(),cap() 函数不适用 map 611 | 612 | ## 26. 下面这段代码输出什么? 613 | 614 | ```go 615 | func main() { 616 | var i interface{} 617 | if i == nil { 618 | fmt.Println("nil") 619 | return 620 | } 621 | fmt.Println("not nil") 622 | } 623 | ``` 624 | 625 | - A. nil 626 | - B. not nil 627 | - C. compilation error 628 | 629 | **答:A** 630 | 631 | **解析:** 632 | 633 | 当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil 634 | 635 | ## 27. 下面这段代码输出什么? 636 | 637 | ```go 638 | func main() { 639 | s := make(map[string]int) 640 | delete(s, "h") 641 | fmt.Println(s["h"]) 642 | } 643 | ``` 644 | 645 | - A. runtime panic 646 | - B. 0 647 | - C. compilation error 648 | 649 | **答:B** 650 | 651 | **解析:** 652 | 653 | 删除 map 不存在的键值对时,不会报错,相当于没有任何作用;获取不存在的减值对时,返回值类型对应的零值,所以返回 0。 654 | 655 | ## 28. 下面属于关键字的是() 656 | 657 | - A. func 658 | - B. struct 659 | - C. class 660 | - D. defer 661 | 662 | **答:A、B、D** 663 | 664 | ## 29. 下面这段代码输出什么? 665 | 666 | ```go 667 | func main() { 668 | i := -5 669 | j := +5 670 | fmt.Printf("%+d %+d", i, j) 671 | } 672 | ``` 673 | 674 | - A. -5 +5 675 | - B. +5 +5 676 | - C. 0 0 677 | 678 | **答:A** 679 | 680 | **解析:** 681 | 682 | `%d`表示输出十进制数字,`+`表示输出数值的符号。这里不表示取反。 683 | 684 | ## 30. 下面这段代码输出什么? 685 | 686 | ```go 687 | type People struct{} 688 | 689 | func (p *People) ShowA() { 690 | fmt.Println("showA") 691 | p.ShowB() 692 | } 693 | func (p *People) ShowB() { 694 | fmt.Println("showB") 695 | } 696 | 697 | type Teacher struct { 698 | People 699 | } 700 | 701 | func (t *Teacher) ShowB() { 702 | fmt.Println("teacher showB") 703 | } 704 | 705 | func main() { 706 | t := Teacher{} 707 | t.ShowB() 708 | } 709 | ``` 710 | 711 | **答:teacher showB** 712 | 713 | **解析:** 714 | 715 | 知识点:结构体嵌套。 716 | 717 | 在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。这个例子中的 ShowB() 就是同名方法。 718 | --------------------------------------------------------------------------------