乐趣区

关于golang:Go-Quiz-从Go面试题看分号规则和switch的注意事项

面试题

这是 Go Quiz 系列的第 3 篇,对于 Go 语言的分号规定和 switch 的个性。

这道题比拟 tricky,通过这道题能够加深咱们对 Go 语言里的分号 : 规定和 switch 个性的了解。

package main

func f() bool {return false}

func main() {switch f() 
  {
    case true:
        println(1)
    case false:
        println(0)
    default:
        println(-1)
    }
}
  • A: 1
  • B: 0
  • C: -1

这道题次要考查以下知识点:

  • Go 语言里的分号 : 规定
  • switch前面的 { 换行后编译器会在背地做什么?

解析

Go 语言和 C++ 一样,在每行语句 (statement) 的开端是以分号 结尾的。

看到这里,你可能会有点懵,是不是在想:我写 Go 代码的时候也没有在语句开端加分号啊。。。

那是因为 Go 编译器的词法解析程序主动帮你做了这个事件,在须要加分号的中央给你加上了分号。

如果你在代码里显示地加上分号,编译器是不会报错的,只是 Go 不须要也不倡议显示加分号,所有交给编译器去主动实现。

那编译器是怎么往咱们代码里插入分号 : 的呢?规定是什么?咱们看看官网文档的说法:

  1. When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line’s final token if that token is

    • an identifier
    • an integer, floating-point, imaginary, rune, or string literal
    • one of the keywords break, continue, fallthrough, or return
    • one of the operators and punctuation ++, --, ), ], or }
  2. To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".

依据这 2 个规定,咱们来剖析下本文最开始的题目,switch代码如下所示:

// 示例 1
switch f() 
{
    case true:
        println(1)
    case false:
        println(0)
    default:
        println(-1)
}

下面的代码对于 switch f() 满足规定 1,会在 ) 前面主动加上分号,等价于示例 2

// 示例 2
switch f();
{
    case true:
        println(1)
    case false:
        println(0)
    default:
        println(-1)
}

示例 2 的代码等价于示例 3,程序运行会进入到 case true 这个分支

// 示例 3
switch f();true {
    case true:
        println(1)
    case false:
        println(0)
    default:
        println(-1)
}

所以本题的答案是 1,抉择A

总结

  1. Effective Go官网倡议:除了 for 循环之外,不要在代码里显示地加上分号

    你可能在 ifswitch关键字前面看到过分号:,比方上面的例子。

    if i := 0; i < 1 {println(i)
    }
    
    switch os := runtime.GOOS; os {
        case "darwin":
            fmt.Println("OS X.")
        case "linux":
            fmt.Println("Linux.")
        default:
            // freebsd, openbsd,
            // plan9, windows...
            fmt.Printf("%s.\n", os)
    }

    尽管代码能够失常工作,然而官网不倡议这样做。咱们能够把分号 后面的变量赋值提前,比方批改为上面的版本:

    i := 0
    if i < 1 {println(i)
    }
    
    os := runtime.GOOS
    switch os {
        case "darwin":
            fmt.Println("OS X.")
        case "linux":
            fmt.Println("Linux.")
        default:
            // freebsd, openbsd,
            // plan9, windows...
            fmt.Printf("%s.\n", os)
    }
  2. {不要换行,如果对于 if, forswitchselect{ 换行了,Go 编译器会主动增加分号:,会导致预期之外的后果,比方本文最结尾的题目和上面的例子。

    // good
    if i < f() {g()
    }
    
    // wrong: compile error
    if i < f()  // wrong!
    {           // wrong!
        g()}
  3. 养成应用 go fmt, go vet 做代码查看的习惯,能够帮咱们提前发现和躲避潜在隐患。

思考题

思考上面这 2 道题的运行后果是什么?大家能够在评论区留下你们的答案。

题目 1:

// Foo prints and returns n.
func Foo(n int) int {fmt.Println(n)
    return n
}

func main() {switch Foo(2) {case Foo(1), Foo(2), Foo(3):
        fmt.Println("First case")
        fallthrough
    case Foo(4):
        fmt.Println("Second case")
    }
}

题目 2:

a := 1
fmt.println(a++)

开源地址

文章和示例代码开源地址在 GitHub: https://github.com/jincheng9/…

公众号:coding 进阶

集体网站:https://jincheng9.github.io/

知乎:https://www.zhihu.com/people/…

好文举荐

  1. 被 defer 的函数肯定会执行么?
  2. Go 有援用变量和援用传递么?map,channel 和 slice 作为函数参数是援用传递么?
  3. new 和 make 的应用区别是什么?
  4. 一文读懂 Go 匿名构造体的应用场景
  5. 官网教程:Go 泛型入门
  6. 一文读懂 Go 泛型设计和应用场景
  7. Go Quiz: 从 Go 面试题看 slice 的底层原理和注意事项
  8. Go Quiz: 从 Go 面试题看 channel 的注意事项

References

  • https://go101.org/quizzes/swi…
  • https://go.dev/doc/effective_go
  • https://go101.org/article/lin…
  • https://yourbasic.org/golang/…
退出移动版