• Golang流程控制


    流程控制

    在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。

    • 顺序控制
    • 分支控制
    • 循环控制

    顺序控制

    程序从上到下逐行的执行,中间没有任何判断和跳转。

    分支控制

    让程序有选择的执行,分支控制有三种:

    • 单分支
        if 条件表达式 {
            //执行代码块
        }
    
    • 双分支
        if 条件表达式 {
            //执行代码块
        } else {
            //执行代码块
        }
    
    • 多分支
        if 条件表达式1 {
            //执行代码块 
        } else if 条件表达式2 {
            //执行代码块
        } else {
            //执行代码块
        }
    
        //在符合一个条件 执行相应代码块后 会结束
    
    1. {}是必须有的,就算你只写一行代码;
    2. 条件表达式不需要小括号括起来
    3. 块内声明的变量的作用域只在该块内
    4. golang支持在if中,直接定义一个变量
        if age := 20; age > 18 { 
            //代码块
        } 
    
    1. if语句的条件表达式不能是赋值语句
        if b = false { //错误
    
        } 
    
    1. if语句的条件表达式的结果必须是bool值
        n := 4 if n { //错误
    
        }
    

    switch

    switch语句用于基于不同条件执行不同动作,每一个case分支都是唯一的,从上到下逐一测试,直到匹配位置。

    匹配项后面也不需要再加break

    基本语法:

        switch 表达式 {
            case 表达式1,表达式2, ... :
                语句块
            case 表达式3,表达式4, ... :
                语句块
            case 表达式5:
                语句块
            default:
                语句块
        }
    
    1. switch的执行流程是,先执行表达式,得到值,然后和case的表达式进行比较,如果相等,就匹配到,然后执行对应的case的语句块,然后退出switch控制,如果一个都匹配不到,则执行default。
    2. default语句不是必须的。
    3. 如果switch的表达式的值没有和任何的case的表达式匹配成功,则执行default的语句块。执行后退出switch的控制。
    4. golang的case后的表达式可以有多个,使用逗号间隔。
    5. golang中的case语句块不需要写break,因为默认会有,即在默认情况下,当程序执行完case语句块后,就直接退出该switch控制结构。
    6. fallthrough:与下面的一个case条件属于逻辑或的关系,相等于给下面的一个case增加了一个逻辑或的条件
    7. case后面的各个表达式的值的数据类型,必须和switch的表达式数据类型一致
    package main
    
    import "fmt"
    
    func main() {
    
        var num1 int32 = 20
        var num2 int64 = 20
    
        switch num1 {
    
        case num2:
            fmt.Println("相等呢")
        case 30:
            fmt.Println("哈哈")
    
        }
    }
    //运行时报错
    invalid case num2 in switch on num1 (mismatched types int64 and int32)
    

    但是如果里面是一个数字,则可以的,因为数字本身是不带类型的

    package main
    
    import "fmt"
    
    func main() {
    
        var num1 int32 = 20
    
        switch num1 {
    
        case 20.0:
            fmt.Println("相等呢")
        case 30:
            fmt.Println("哈哈")
    
        }
    }
    
    1. golang中switch后面的表达式甚至不是必须的
    2. Type switch
      switch语句还可以被用于type-switch来判断某个interface变量中实际存储的变量类型。
      Type Switch语法格式如下:
        switch x.(type) {
        case type:
            statement(s);
            case type:
            statement(s);
            //你可以定义任意个数的case
            default: /*可选*/
            statement(s);
        }
    

    示例:

        package main
    
        import "fmt"
    
        func main() {
    
            var x interface{}
    
            switch i := x.(type) { //x.()格式是类型断言
            case nil:
                fmt.Printf("x 的类型是: %T", i)
            case int:
                fmt.Printf("x 的类型是: int")
            case float64:
                fmt.Printf("x的类型是: float64")
            case func(int) float64:
                fmt.Printf("x的类型是: func(int)")
            case bool, string:
                fmt.Printf("x的类型是: bool或string")
            default:
                fmt.Printf("未知型")
            }
        }
        //以上代码的执行结果为:
        x 的类型是: <nil>
    
    1. case后是一个表达式(即:常量值、变量、一个有返回值的函数等都可以)
    2. case后面的表达式如果是常量值(字面量),则要求不能重复
        package main
    
        import "fmt"
    
        func main() {
    
            var n1 int32 = 5
            var n2 int32 = 20
            var n3 int32 = 5
            switch n1 {
    
                case n2, 10, 5:
                    fmt.Println("case1")
                case 5: //这里不允许重复出现数字5,但是如果我们把5替换成变量n3就不会报错
                    fmt.Println("case2")
            }
        }
    
    1. switch后面也可以直接声明/定义一个变量,分号结束,不推荐
        switch grade := 90; {
            case grade > 90:
            fmt.Println("成绩优秀...")
            case grade >= 60 && grade <= 90:
            fmt.Println("成就优良")
            default:
            fmt.Println("不及格")
        }
    
    1. switch和if的比较

    如果判断的具体数值不多,而且符合整数、浮点数、字符、字符串这几种类型,建议使用switch语句,简洁高效。
    其他情况:对区间判断和结果为bool类型的判断,使用if,if使用的范围更广

    循环控制

    Go语言中的循环语句只支持for关键字,不支持while和do-where结构。

    for

    语法:
    Go语言的for循环有三种形式,只有其中的一种使用分号。

    • 和C语言的for一样:
      • init:一般为赋值表达式,给控制变量赋初值;

      • condition:关系表达式或逻辑表达式;循环控制条件

      • post:一般为赋值表达式,给控制变量增量或减量。

    for init; condition; post {}
    
    • 和C的while一样:
      • 将变量初始化和变量迭代写到其它位置
    for condition {}
    
    • 和C的for(;;)一样:
      • 如果for循环内部没有break语句,它会一直循环下去, 通常需要配合 break 语句使用
    for {}
    
    • for循环的range格式可以对slice、map、数组、字符串等进行迭代循环。格式如下:
      • 字符串遍历方式 1-传统方式
          var str string = "hello, world"
          for i := 0; i < len(str); i++ {
              fmt.Printf("%c 
      ", str[i])
          }
      
      • 字符串遍历方式 2-for - range
          var str string = "hello, world"
          for key, value := range str {
              fmt.Printf("key=%d, value=%c 
      ", key, value)
          }
      

    问题:如果我们的字符串含有中文,那么传统的遍历字符串方式,就是错误,会出现乱码。原因是传统的对字符串的遍历是按照字节来遍历,而一个汉字在 utf8 编码是对应 3 个字节。
    如何解决:需要要将 str 转成 []rune 切片. 对应 for-range 遍历方式而言,是按照字符方式遍历。因此如果有字符串有中文,也是可以的

    break

    break语句用于终止某个语句块的执行,用于中断当前for循环或跳出switch语句。break是跳出整个循环。

    break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块。

        package main
        import "fmt"
    
        func main() {
            //label1:
            for i := 0; i < 4; i++ {
                label2:
                for j := 0; j < 5; j++ {
                    if j == 2 {
                        //break  //break默认会跳出最近的循环
                        //break label1;
                        break label2;
                    }
                    fmt.Println("j = ", j)
                }
            }
        }
    

    continue

    continue语句用于结束本次循环,继续执行下一次循环。

    continue语句出现在多层嵌套的循环语句体中时,可以通过标签知名要跳过的是哪一层循环,这个和前面的break + 标签的使用规则一样。

    goto

    • Go语言的goto语句可以无条件地转移到程序中指定的行。
    • goto语句通常与条件语句配合使用。可以用来实现条件转移,跳出循环体等功能。
    • 在Go程序设计中一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序都产生困难。
      语法:
        goto label
    
        label: statement
    

    示例:

        package main
        import "fmt"
    
        func main() {
            fmt.Println(1);
            goto label1
            fmt.Println(2);
            fmt.Println(3);
            fmt.Println(4);
    
            label1:
            fmt.Println(5);
        }
        //输出结果
        1
        5
    

    return

    return 使用在方法或者函数中,表示跳出所在的方法或函数

    1. 如果 return 是在普通的函数,则表示跳出该函数,即不再执行函数中 return 后面代码,也可以理解成终止函数。
    2. 如果 return 是在 main 函数,表示终止 main 函数,也就是说终止程序。

    defer

    在函数返回之前, 调用defer函数的操作, 简化函数的清理工作.

    1. 在defer表达式确定的时候,defer修饰的函数(后面统称为defered函数)的参数也就确定了
    2. 函数内可以有多个defered函数,但是这些defered函数在函数返回时遵守后进先出的原则
    3. 函数命名的返回值跟defered函数一起使用

    函数的返回值有可能被defer更改,本质原因是return xxx语句并不是一条原子指令,执行过程是: 保存返回值(若有)-->执行defer(若有)-->执行return跳转。

    panic (相当于抛出异常)

    Panic是一个可以停止程序执行流程的内置函数。

    1. 调用方函数执行从当前调用点退出
    2. 通过panic可以设定返回值

    panic存在的意义,不仅可以控制异常处理流程,还可以用来返回异常原因。
    如果panic不给调用方返回异常原因,那么调用方就无从下手处理问题。 因此在调用panic时,一般来说都是返回一个字符串,用来标示失败原因。
    panic的返回值,通过recover函数来获取。

    1. 在调用panic之前defer的操作会在调用panic后立即执行。

    recover (相当于捕获异常)

    recover函数也是一个内置函数,专门用来接收panic函数返回值。当panic函数没有被调用或者没有返回值时,recover返回Nil.
    捕获函数 recover 只有在延迟调⽤内直接调⽤才会终⽌错误,否则总是返回 nil。任何未捕获的错误都会沿调⽤堆栈向外传递。

  • 相关阅读:
    太忙了
    Delphi 的接口(2) 第一个例子
    Delphi 的接口(3) 关于接口的释放
    VS.NET让我做了一场恶梦
    [推荐阅读]The Best Of .The NET 1.x Years
    向大家说声对不起
    [致歉]16:30~17:10电信网络出现问题
    服务器恢复正常
    [SharePoint]更改活动目录(AD)中用户名的问题
    [正式决定]博客园开始接受捐助
  • 原文地址:https://www.cnblogs.com/KylinBlog/p/13607249.html
Copyright © 2020-2023  润新知