1. Go语言顺序程序设计,错误处理

版权声明:博客仅作为博主的个人笔记,未经博主允许不得转载。 https://blog.csdn.net/qq_35976351/article/details/81782424

变量

声明

Go语言的变量声明需要类型后置

var v1 int
var v2 string
var v3 [10]int  // 数组
var v4 []int    // 数组切片

初始化和赋值

var v1 int = 10  // 显式说明,同时赋值
var v2 = 10      // 自动推导
v3 := 10         // 自动推导

变量声明和赋值是不同的

var v10 int
v10 = 123

多重赋值的功能:

i, j = j, i   // 相当于交换变量的值

匿名变量

使用下划线即可,类比于python

func GetName() (firstName, lastName, nickName string) {
    return "May", "Chen", "bilibili"
}
_, _, nickName := getName()  // 下划线作为匿名变量

常量

字值值

编译期间就一直而且不可改变的值,可以是数值类型、布尔类型、字符串类型等。

常量的const定义

const Pi float64 = 3.1415926
const zero = 0.0              // 无类型的 
const (
    size int64 = 1024
    eof = -1
)
const u, v float32 = 0, 3     // 多重赋值
const a, b, c = 3, 4, "foo"   // a==3 b==4 c=="foo"

常量在编译期间就会确定了,可以使无类型的。

itoa是自动增长的,每遇到const后就重设为0

const (         // itoa重设为0
    c0 = itoa   // c0 == 0
    c1 = itoa   // c1 == 1
    c2 = itoa   // c2 == 2
)
const (
    a = 1 << itoa  // 1
    b = 1 << itoa  // 2
    c = 1 << itoa  // 4
)

如果两个const的赋值语句的表达式是一样的,那么可以省略后一个表达式,上述的可以改写为:

const (         // itoa重设为0
    c0 = itoa   // c0 == 0
    c1          // c1 == 1
    c2          // c2 == 2
)
const (
    a = 1 << itoa  // 1
    b = 1          // 2
    c = 1          // 4
)

枚举

枚举和上面的组内定义的const一致,Go中没有enum关键字,小写字母开头在包外不可见。枚举是没有类型的。

const (
    Sun = itoa
    Mon
    Tue
    Web
    Thu
    Fri
    Sat
    numberOfDays  // 没导出
)

类型

内置类型

包括整型、浮点型、布尔型、字符串等,布尔型无法进行类型转换,其余的可以参考有关资料。新增了一个复数类型complex64可以参考文档。。。字符串在初始化后,无法更改内容。

数组

声明方法,与变量类似,都是元素类型后置:

[32]byte                       // 长度为32字节的数组
[2*N]struct{x, y, int32}       // 结构体数组
[1000]*float64                 // 指针数组
[3][5]int                      // 二位数组 3*5

实际的使用方式,给数组特定的名称:

var arr1 [10]int                    // 声明
var arr2 = [5]int{1, 2, 3, 4, 5} // 声明并初始化

访问方式:

for i := 0; i<len(array); ++i{   // 下标遍历方式
}
for i, v := range array{  // 容器方式,i表示下标,v表示元素   
    // 对v的所有操作不影响array的值,只是原来元素的副本
}

与C/C++不同,数组作为函数的参数传递时,是按值传递的,必须使用数组切片才能传入原来的数组!

func foo(array [10]int) {
    // 无法对原来的数组进行更改,只是原来数据的副本
}

数组可以直接进行赋值操作,但是也是赋值的副本,这与C、C++有区别:

a := [3]int{1, 2, 3}
b := a

ba数组有相同的数值,但是b不是a的引用或者地址,b是复制的a的值,更改b的不会影响a的值。除非是使用切片。

数组切片

数组切片可以理解为指向数组的指针,同时拥有自己的数据结构。类似于C++的std::vector,内部存储的还是原来数组的元素,如果对切片进行更改,就是更改原来数组的元素。

基于数组创建:

var array = [5]int{1 2 3 4 5}
mySlice := array[:]   // 切取所有元素
mySlice1 := array[:4] // 切取前4个
mySlice2 := array[1:] // 切取后4个
mySlice3 := array[1:3]// 切取数据2 3

直接创建:

mySlice1 := make([]int, 5)       // 元素个数为5的切片,初始值为0
mySlice2 := make([]int, 5, 10)   // 初始元素个数为5,初始值为0,预留10个元素的存储空间
mySlice3 := []int{1, 2, 3, 4, 5} // 直接创建

在直接创建的过程中,其实使用一个匿名的数组被创建出来的。。。。。

遍历方法与数组一致。。。

数组切片动态的增减元素时,可以使用append方法

mySlice := []int{8, 9, 10}
mySlice1 = append(mySlice, 1, 2, 3)
mySlice2 = append(mySlice, mySlice1...) // ...表示把元素打散后传入

使用copy()函数进行内容复制。

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3} 
copy(slice2, slice1)   // 只会复制前3个
copy(slice1, slice2)   // 只会复制3个 

map

是一个内置的数据类型。。

使用方式:

type PersonInfo struct{
    ID string
    Name string
    Address string
}
var myMap map[string] PersonInfo     // 声明
myMap = make(map[string] PersonInfo) // 创建,空的没有元素
myMap1 := make(map[string] PersonInfo, 100) // 指定了初始化的容量
myMap2 := map[string]PersonInfo{            // 带有初始值的初始化
    "1234": PersonInfo{"1", "Jack", "Room 101, ..."}
}
delete(myMap, "1234")  // 删除键值是1234的元素
value, ok := myMap["1234"] // 查找元素
if ok { // 找到了
    // 处理找到的元素
}

流程控制

switch

每个case最后不用添加break关键字,自动跳出的。如果想要接着执行下一个case,则需要使用fallthrough关键字。一个case可以匹配多个条件。

    switch i {
    case 1:
    case 2:
        fallthrough
    case 3, 4, 5:
    default:

    }

循环

基本类似C/C++,主要区别是在for上:

无限循环:

for{
    // do smoething
}

支持多重赋值:

for i, j := 0, 100; i < j; i, j = i + 1, j - 1 {
    // do something
} 

支持goto语句,与C语言一致。

    for j := 0; j < 5; j++ {
        for i := 0; i < 10; i++ {
            if i > 5 {
                goto JLoop
            }
        }
    }
    JLoop:

函数

函数定义的一般格式:

func func_name (arguements) (return values){
    // func body
}

参数和返回值都是类型后置的。Go语言函数的参数都是按值传递的,要想改变原来的值,需要传递指针,这与C语言一样。

包外调用的函数的首字母必须大写才可见,小写只有包内可见。。

支持不定参数,类比于C++的 initializer_list,但是这里的更高级,可以支持多类型。不定参数必须放在参数的最后才可以,...type本质上是数组切片!

固定类型的不定参数:

func myfunc(args ...int) {
    for _, arg := range args {
        fmt.Println(arg)
    }
}

任意类型的不定参数,需要类型的指定判断。

func MyPrintf(args ...interface{}) {
    for _, arg := range args {
        switch arg.(type) {
        case int:
            // do something
        case string:
            // do something
        default: 
            // do something
        }
    }
}

函数可以返回多个值,函数执行不带任何参数的return语句时,会返回对应的返回值变量的值。

Go语言支持匿名函数

    f := func(a, b int, z float32) bool {
        return a*b < int(z)
    }()   // 匿名函数返回值,最后带有()表示直接调用

错误处理

error接口

Go语言有一个标准的错误处理魔术,error的接口定义如下:

type error interface {
    Error() string
}

在写函数的时候,一般把error作为最后一个返回值,一般的错误处理方法是:

func Foo(param int) (n int, err error) {
    // function body
}

// 处理错误的方式
n, err := Foo(0)
if err!=nil{
// deal with error
}else{
// use return value n
}

具体实践应用留作后期补充

defer

类似于C++中析构函数的,实际上适用于处理需要释放的资源。

unc CopyFile(dst, src string) (w int64, err error) {
    srcFile, err := os.Open(src)
    if err != nil {
        return
    }
    defer srcFile.Close()

    dstFile, err := os.Create(dst)
    if err != nil {
        return
    }

    defer dstFile.Close()

    return io.Copy(dstFile, srcFile)
}

在函数中,先defer声明的,后释放,类似于构造函数的顺序。如果需要释放的资源过于繁杂,可以使用匿名函数的方式:

defer func() {
    // 清理工作
}

panicrecover

两者结合报告和处理运行时的错误,作用有点像trycatch。当出现panic的时候,正常的函数流程结束,但是defer会继续执行。panic会沿着defer向上传递,直到遇见recover后者整个程序流程终止。panic可以传入任何参数,recover可以截获panic传入的参数,并进行异常处理。

package main

import "fmt"

func main() {
    defer func() { // 必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("c")
        if err := recover(); err != nil {
            fmt.Println(err) // 这里的err其实就是panic传入的内容,55
            // 实际的工作流程中,在这里进行异常代码的处理
        }
        fmt.Println("d")
    }() // 末尾的括号表示defer发生时,立刻进行调用匿名函数
    f() // 测试函数
}

func f() {
    fmt.Println("a")
    panic(55)         // 假设这里出现了异常
    // 因为调用了panic,因此下面的都不执行,程序中断。
    fmt.Println("b")
    fmt.Println("f")
}
/*
输出结果:
a
c
55
d
*/

panicrecover就是异常处理,但是会影响程序的性能。因此,如果可以使用error的方式简单处理,就不用这种方法。。

猜你喜欢

转载自blog.csdn.net/qq_35976351/article/details/81782424