FreezeJ' Blog

GO基础代码

2022-03-08

最近看了一下GO语言,学习一下。记录一下基础代码块,方便查阅。
参考教程:
https://www.runoob.com/go
http://c.biancheng.net/golang

变量声明

package main

import "fmt"

// 多行声明
var (
    a int
    b bool
    c = "123"
)

func main() {
    x := 1
    fmt.Println(x)
    test := "test"
    fmt.Println(test)
    var x1, x2, x3 = "1", "2", "3"
    fmt.Println(x1, x2, x3)
    y1, y2, y3 := "4", "5", "6" // 这种不带声明的只能出现在函数体
    fmt.Println(y1, y2, y3)
    fmt.Println(a, b, c)
    /*
        在Go中,声明的变量必须使用,否则不能通过编译。
        下划线"_"在Go中表示一个空白标识符,它是一个只写变量,可以把不需要的变量赋值给它。
    */
    _, a := 1, 2
    fmt.Println(a)
}

变量打印

package main

import "fmt"

func main() {
    var test string = "test" // string变量类型可以省略,编译器会根据所赋的值来识别
    fmt.Println(test)

    var x, y int = 1, 2
    fmt.Println(x, y)
}

变量空值

package main

import "fmt"

func main() {
    var varInt int
    var varStr string
    var varFloat64 float64
    var varBool bool
    var varList []int
    fmt.Println(varInt, varStr, varFloat64, varBool, varList)
}

类型转换

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func main() {
    a := 123
    b := float32(a)
    // 数字转字符串
    c := strconv.Itoa(a)
    // 字符串转数字
    d, _ := strconv.Atoi(c)
    e, _ := strconv.ParseInt(c, 10, 64)
    fmt.Println(reflect.TypeOf(a), a)
    fmt.Println(reflect.TypeOf(b), b)
    fmt.Println(reflect.TypeOf(c), c)
    fmt.Println(reflect.TypeOf(d), d)
    fmt.Println(reflect.TypeOf(e), e)
}

常量

package main

import "fmt"

// 用作枚举
const (
    Unknown = 0
    Female  = 1
    Male    = 2
)

// 常量是一个简单值的标识符,在程序运行时,不会被修改的量
func main() {
    const a = "test"      // string类型,使用双引号
    const b = 'A'         // char类型,使用单引号
    const c, d = '1', '2' // 常量可以声明定义,并且不使用
    fmt.Println(a)
    fmt.Println(b) // 打印的是ASCII码

    // iota常量索引,每次 const 出现时,都会让 iota 初始化为0
    const x = iota // x=0
    const (        // 出现const,iota初始化为0
        y = iota //y=0
        z        //z=1   相当于z=iota
    )
}

数组

package main

import "fmt"

func printGroup(group []int) { // 这里接收参数的类型必须和定义时一样
    fmt.Println("数组长度:", len(group))
    for i := range group {
        fmt.Println(i)
    }
}

// 与Python的数组不同,GO语言中的数组长度不可改变
func main() {
    group1 := [5]int{1, 2, 3} // 不足元素补充空值
    fmt.Println(group1)
    group1[3] = 4 // 赋值
    fmt.Println(group1)
    group2 := []int{1, 2, 3}       // 不定长度使用...,编译器自动计算长度
    group3 := []int{1, 2, 3, 4, 5} // 不定长度使用...,编译器自动计算长度
    printGroup(group2)
    printGroup(group3)
}

切片slice

package main

import "fmt"

// slice相当于动态数组
func main() {
    var s []int   // 未指定长度的数组其实就是切片
    if s == nil { // 判断空切片
        fmt.Println("空切片:", s)
    }
    s = make([]int, 10, 20) // 数组长度10,容量20
    fmt.Println("初始化切片:", s)
    for i, _ := range s {
        s[i] = i
    }
    fmt.Println("切片循环赋值:", s)
    fmt.Println("切片截取:")
    fmt.Println(s[2:5])
    fmt.Println(s[:5])
    fmt.Println(s[5:])
    fmt.Println(s[5 : len(s)-2]) // 好像没有直接s[5:-2]的写法
    fmt.Println(s)
    fmt.Println("cap容量:", cap(s))
    fmt.Println("len长度:", len(s))
    for i := 0; i < 50; i++ {
        // 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
        s = append(s, 10+i) // 插入元素
    }
    fmt.Println(s)
    fmt.Println("cap容量:", cap(s)) // 容量不足时会自动扩容,但是容量和长度不是同等的计算方式
    fmt.Println("len长度:", len(s))

    // 拷贝切片,目标在前
    var n []int
    n = make([]int, 10, 20)
    copy(n, s)
    fmt.Println("拷贝的切片n:", n)
    copy(n, []int{0, 0, 0, 0, 0})
    fmt.Println("拷贝的切片n:", n)
    fmt.Println("拷贝的切片s:", s)

    // 删除元素
    slice1 := []int{1, 2, 3, 4, 5}
    fmt.Println(slice1)
    // 删除第一个元素
    slice2 := slice1[1:]
    fmt.Println(slice2)
    // 删除最后一个元素
    slice3 := slice1[:len(slice1)-1]
    fmt.Println(slice3)
    // 删除第x个元素(n<=len)
    x := 2
    slice4 := append(slice1[:x-1], slice1[x:]...)
    fmt.Println(slice4)
}

range

package main

import "fmt"

// range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
func main() {
    // 遍历数组
    numArr := [...]int{1, 2, 3}
    var sum int
    for index, num := range numArr {
        fmt.Printf("第%d个数字是:%d\n", index+1, num)
        sum += num
    }
    fmt.Println("sum:", sum)

    // 遍历切片,循环10次
    count := 0
    for range make([]int, 10) {
        fmt.Println(count)
        count++
    }

    // 遍历字符串
    for _, c := range "hello world" {
        fmt.Println(c)
    }
}

map

package main

import "fmt"

func main() {
    // 声明一个map,key为string类型,value也是string类型
    weekMap := map[string]string{
        "Mon":  "星期一",
        "Tues": "星期二",
        "Wen":  "星期三",
        "Thur": "星期四",
        "Fri":  "星期五",
        "Sat":  "星期六",
        "Sun":  "星期天",
    }
    fmt.Println(weekMap)
    fmt.Println(weekMap["Mon"])
    // 删除元素
    delete(weekMap, "Sun")
    fmt.Println(weekMap)
    // 插入元素
    weekMap["Sun"] = "星期天"
    fmt.Println(weekMap)
}

并发map

package main

import (
    "fmt"
    "sync"
)

func main() {
    syncMap := sync.Map{}
    // 存储值
    syncMap.Store("test1", 1)
    // 可以储存不同的类型
    syncMap.Store("test2", "2")

    // 读取值
    fmt.Println(syncMap.Load("test1"))
    fmt.Println(syncMap.Load("test2"))

    // 遍历
    syncMap.Range(func(key, value interface{}) bool {
        fmt.Println(key)
        fmt.Println(value)
        return true // 返回false就不继续循环
    })
}

指针

package main

import "fmt"

func swapPoint(a, b *int) {
    var tmp int
    tmp = *a
    *a = *b
    *b = tmp
}

func main() {
    a := 10
    var ip *int
    fmt.Println("空指针:", ip)
    ip = &a
    fmt.Println("a的值:", a)
    fmt.Println("a的地址:", &a)
    fmt.Println("ip的值:", ip)
    fmt.Println("ip的指向:", *ip)

    // 指针数组
    const MAX = 3
    arr := [MAX]int{1, 2, 3}
    var ptr [MAX]*int
    for i := 0; i < MAX; i++ {
        ptr[i] = &arr[i]
    }
    fmt.Println("指针数组的值:", ptr)
    for i := range ptr {
        fmt.Println("指向的值:", i)
    }

    // 指向指针的指针
    var v int
    var vptr *int
    var vpptr **int
    v = 300
    vptr = &v
    vpptr = &vptr
    fmt.Println("v的值", v)
    fmt.Println("ptr的值", *vptr)
    fmt.Println("pptr的值", **vpptr)

    // 向函数传递指针
    x1, x2 := 1, 9
    var ptr1, ptr2 *int
    ptr1 = &x1
    ptr2 = &x2
    fmt.Println(x1, x2)
    swapPoint(ptr1, ptr2)
    fmt.Println(x1, x2)
}

结构体

package main

import "fmt"

type Books struct {
    name   string
    author string
    pages  uint8
}

func (b Books) print() {
    fmt.Printf("书本名称: %s\n作者: %s\n页码: %d\n", b.name, b.author, b.pages)
}

func main() {
    b1 := Books{
        name:   "Hello",
        author: "FreezeJ",
        pages:  80,
    }
    b2 := Books{
        name:   "World",
        author: "FreezeJ",
        pages:  38,
    }
    b1.print()
    b2.print()

    // 结构体指针
    var bp1, bp2 *Books
    bp1, bp2 = &b1, &b2
    // 地址
    fmt.Println(bp1)
    fmt.Println(bp2)
    // 值
    fmt.Println(*bp1)
    fmt.Println(*bp2)
    // 引用内容
    fmt.Println(bp1.name)
    fmt.Println(b1.name)
    fmt.Println(bp2.name)
    fmt.Println(b2.name)
}

switch

package main

import (
    "fmt"
    "strconv"
)

const (
    Mon  = "Monday"
    Tues = "Tuesday"
    Wen  = "Wednesday"
    Thur = "Thursday"
    Fri  = "Friday"
    Sat  = "Saturday"
    Sun  = "Sunday"
)

func main() {
    var day string
    fmt.Println("请输入:")
    fmt.Scanf("%s", &day)

    // 形式一
    switch day {
    case Mon:
        fmt.Println("星期一")
    case Tues:
        fmt.Println("星期二")
    case Wen:
        fmt.Println("星期三")
    case Thur:
        fmt.Println("星期四")
    case Fri:
        fmt.Println("星期五")
    case Sat:
        fmt.Println("星期六")
    case Sun:
        fmt.Println("星期天")
    default:
        fmt.Println("日期错误")
        return
    }

    // 形式二,类似于使用if...else...
    switch {
    case day == Sat, day == Sun:
        fmt.Println("周末")
    default:
        fmt.Println("要上班")
    }

    // fallthrough关键字,使用后case匹配不跳出,继续执行下一个case
    leftDay := 0
    switch day {
    case Mon:
        leftDay++
        fallthrough
    case Tues:
        leftDay++
        fallthrough
    case Wen:
        leftDay++
        fallthrough
    case Thur:
        leftDay++
        fallthrough
    case Fri:
        leftDay++
    }
    if leftDay > 0 {
        fmt.Println("还有" + strconv.Itoa(leftDay) + "天放假!")
    }
}

if语句

package main

import "fmt"

func main() {
    var value int
    fmt.Scan(&value) // 输入数字
    if value > 1 {
        fmt.Println("大于1")
    } else if value > 999 {
        fmt.Println("大于999")
    } else {
        fmt.Println("小于或等于1")
    }
}

循环

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i <= 10; i++ {
        sum += i
    }
    fmt.Println(sum)

    // 类似于while
    count := 1
    for count < 10 {
        fmt.Printf("计数器:%d\n", count)
        count += 1
    }

    // for each 形式
    strings := []string{"google", "baidu", "bing"}
    for i, x := range strings {
        fmt.Println(i, x)
    }

    // 嵌套循环:九九乘法表
    for x := 1; x < 10; x++ {
        for y := 1; y <= x; y++ {
            z := x * y
            fmt.Printf("%d*%d=%d ", x, y, z)
        }
        fmt.Println("")
    }

    // break语句
    fmt.Println("等于5时跳出:")
    for i := 0; true; i++ {
        fmt.Printf("%d\n", i)
        if i == 5 {
            break
        }
    }

    // continue语句
    fmt.Println("10以内的单数:")
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            continue
        }
        fmt.Printf("%d\n", i)
    }

    // goto语句
    n := 1
AddN:
    n++
    if n < 10 {
        goto AddN
    } else {
        fmt.Println(n)
    }
}

函数

package main

import "fmt"

// 数字乘以2
func double(i int) int {
    return 2 * i
}

func swap(a *int, b *int) {
    fmt.Println("a的内存地址:", a)
    fmt.Println("a的值:", *a)
    fmt.Println("b的内存地址:", b)
    fmt.Println("b的值:", *b)
    var c int
    c = *b
    *b = *a
    *a = c
    fmt.Println("a的内存地址:", a)
    fmt.Println("a的值:", *a)
    fmt.Println("b的内存地址:", b)
    fmt.Println("b的值:", *b)
}

func hello() {
    fmt.Println("hello!")
}

func doSomething(data string, work func(string)) {
    work(data)
}

// 可变参数
func myFunc(args ...string) {
    for _, arg := range args {
        fmt.Println(arg)
    }
}

func main() {
    x, y, z := 2, 6, 3
    fmt.Println("x, y, z:")
    fmt.Printf("%d %d %d\n", x, y, z)
    // 函数返回值
    fmt.Println("double x, y, z:")
    fmt.Printf("%d %d %d\n", double(x), double(y), double(z))

    // 使用引用修改值
    a, b := 33, 44
    fmt.Println("交换前", a, b)
    swap(&a, &b)
    fmt.Println("交换后", a, b)

    // 函数保存为变量
    var h func()
    h = hello
    h()

    // 匿名函数声明并调用
    func(data string) {
        fmt.Println("hello!", data)
    }("world")

    // 匿名函数保存为变量
    f := func() {
        fmt.Println("good!")
    }
    f() // 调用匿名函数

    // 匿名函数用作回调函数
    doSomething("test", func(data string) {
        fmt.Println(data)
    })

    // 可变参数
    myFunc("1", "2", "3")
    strArray := []string{"4", "5", "6"}
    myFunc(strArray...)
}

接口

package main

import "fmt"

type Phone interface {
    call()
}

type IOS struct {
    version int
}

func (ios IOS) call() {
    fmt.Println("IOS version:", ios.version)
}

type Android struct {
    version int
}

func (android Android) call() {
    fmt.Println("Android version:", android.version)
}

func main() {
    var p1 Phone
    var p2 Phone
    p1 = IOS{version: 15}
    p2 = Android{version: 14}
    p1.call()
    p2.call()
}

错误处理

package main

import (
    "fmt"
)

// DIV_ERR 自定义错误信息结构
type DIV_ERR struct {
    etype int // 错误类型
    v1    int // 记录下出错时的除数、被除数
    v2    int
}

// 实现接口方法 error.Error()
func (div_err DIV_ERR) Error() string {
    if 0 == div_err.etype {
        return "除零错误"
    } else {
        return "其他未知错误"
    }
}

// 除法
func div(a int, b int) (int, *DIV_ERR) {
    if b == 0 {
        // 返回错误信息
        return 0, &DIV_ERR{0, a, b}
    } else {
        // 返回正确的商
        return a / b, nil
    }
}
func main() {
    // 正确调用
    v, r := div(100, 2)
    if nil != r {
        fmt.Println("(1)fail:", r)
    } else {
        fmt.Println("(1)succeed:", v)
    }
    // 错误调用
    v, r = div(100, 0)
    if nil != r {
        fmt.Println("(2)fail:", r)
    } else {
        fmt.Println("(2)succeed:", v)
    }
}

panic和recover

package main

import "fmt"

// 任何崩溃都表明了我们的代码中可能存在漏洞,所以对于大部分漏洞,我们应该使用Go语言提供的错误机制,而不是 panic。

func testPanic() {
    defer func() {
        err := recover()
        if err != nil {
            fmt.Println("recover")
            fmt.Println(err)
        }
    }()
    fmt.Println("start")
    panic("raise panic")
    fmt.Println("end")
}

func main() {
    testPanic()
}

并发

package main

import (
    "fmt"
    "math/rand"
    "time"
)

// 数据生产者
func producer(header string, channel chan<- string) {
    // 无限循环, 不停地生产数据
    for {
        // 将随机数和字符串格式化为字符串发送给通道
        channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
        // 等待1秒
        time.Sleep(time.Second)
    }
}

// 数据消费者
func customer(channel <-chan string) {
    // 不停地获取数据
    for {
        // 从通道中取出数据, 此处会阻塞直到信道中返回数据
        message := <-channel
        // 打印数据
        fmt.Println(message)
    }
}
func main() {
    // 创建一个字符串类型的通道
    channel := make(chan string)
    // 创建producer()函数的并发goroutine
    go producer("cat", channel)
    go producer("dog", channel)
    // 数据消费函数
    customer(channel)
}
Tags: GO