Go 基础

date
Apr 11, 2021
slug
27
status
Published
tags
Go
summary
type
Post

变量创建

var <name> <type>

隐式初始化

  • string 类型就初始化为空字符串
  • int 类型就初始化为0
  • float 就初始化为 0.0
  • bool类型就初始化为false
  • 指针类型就初始化为 nil

显示初始化

var name string = "string123" // 或者
var name = "Go编程时光"

多个变量一起声明

var (
    name string
    age int
    gender string
)

:=

name := "Go"
// 等价于
var name string = "Go"
// 等价于
var name = "Go"

声明和初始化多个变量

name, age := "wangbm", 28
交换变量
b, a = a, b

new 函数声明一个指针变量

package main

import "fmt"

func main()  {
    var age int = 28
    var ptr = &age  // &后面接变量名,表示取出该变量的内存地址
		ptr := new(int) // 等价
		var ptr *int // 等价
    fmt.Println("age: ", age)
    fmt.Println("ptr: ", *ptr)
}

类型

整型与浮点型

 
notion image

进制

var num01 int = 0b1100
var num02 int = 014 // 0O14 0o14 00014 都可以
var num03 int = 0xC
 

浮点型

略.
目前认为浮点数, 在满足精度要求的情况下依然是很可靠的.

byte、rune与字符串

byte,占用1个节字,就 8 个比特位(2^8 = 256,因此 byte 的表示范围 0->255),所以它和 uint8 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。
他们可以互相赋值.
package main

import "fmt"

func main() {
	var a byte = 65
	// 8进制写法: var a byte = '\101'     其中 \ 是固定前缀
	// 16进制写法: var a byte = '\x41'    其中 \x 是固定前缀

	var b uint8 = 66
	a = b
	fmt.Printf("a 的值: %c \nb 的值: %c", a, b)

	// 或者使用 string 函数
	// fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b))
}
rune,占用4个字节,共32位比特位,所以它和 uint32 本质上也没有区别。它表示的是一个 Unicode字符.
unicode字符 2^16 == 65536 共17个平面所以需要 32位4字节表示

字符串

双引号括起来就是一个字符串
var mystr string = "hello"

import (
    "fmt"
)

func main() {
    var mystr01 string = "hello"
    var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111}
    fmt.Printf("mystr01: %s\n", mystr01)
    fmt.Printf("mystr02: %s", mystr02)
}
其实是一个 byte数组. utf-8 编码, hello,中国 占用5 + 1 + 3 * 2 = 12个字节.

也可以用反引号. 和双引号的区别是, 忽略转移, 也可以自动换行,类似于python中的r'' + ''' '''
var mystr02 string = `\r\n`
// 相当于
var mystr01 string = "\\r\\n"

var mystr01 string = `123
456

数组

由于长度固定,使用并不广泛.
数组一定要标明长度[3]或者[...]
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3

var arr [3]int = [3]int{1,2,3}

arr := [3]int{1,2,3}

arr := [...]int{1,2,3}

arr:=[4]int{2:3} // 表示[0 0 3 0]

type

import (
    "fmt"
)

func main() {
    type arr3 [3]int

    myarr := arr3{1,2,3}
    fmt.Printf("%d 的类型是: %T", myarr, myarr)
}

切片

切片是对数组的一个连续片段的引用,所以切片是一个引用类型. 可以引用数组的一部分,也可能是整个数组.
import (
    "fmt"
)

func main() {
    myarr := [...]int{1, 2, 3} // 这个是数组
    fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) // 这个是切片
}
定义切片, 要么是从数组来, 要么字面量[]<类型>, 要么 make( []Type, size, cap ) ,
// 定义一个数组
myarr := [5]int{1,2,3,4,5}
mysli1 := myarr[1:3] //[2,3]
mysli2 := myarr[1:3:4] // [2,3] 改变切片容量 基本没啥用


// 声明字符串切片
var strList []string
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{}
a := []int{4:2}

//------------------------------ 
import (
 "fmt"
)

func main() {
 a := make([]int, 2)
 b := make([]int, 2, 10)
 fmt.Println(a, b)
 fmt.Println(len(a), len(b))
 fmt.Println(cap(a), cap(b))
}
切片的零值(默认值)是 nil, 因为他是引用类型.
append的用法
import (
    "fmt"
)

func main() {
    myarr := []int{1}
    // 追加一个元素
    myarr = append(myarr, 2)
    // 追加多个元素
    myarr = append(myarr, 3, 4)
    // 追加一个切片, ... 表示解包,不能省略
    myarr = append(myarr, []int{7, 8}...)
    // 在第一个位置插入元素
    myarr = append([]int{0}, myarr...)
    // 在中间插入一个切片(两个元素)
    myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...)
    fmt.Println(myarr)
}

切片可以直接访问"越界"元素

如果容量足够大
package main

import (
	"fmt"
)

func main() {
	var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	myslice := numbers4[4:6:8]
	fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice))

	myslice = myslice[:cap(myslice)]
	fmt.Printf("myslice的第四个元素为: %d", myslice[3])
}

字典

map[KEY_TYPE]VALUE_TYPE
定义

var scores map[string]int = map[string]int{"english": 80, "chinese": 85}
//或者
scores := map[string]int{"english": 80, "chinese": 85}
//或者
scores := make(map[string]int)
scores["english"] = 80
scores["chinese"] = 85
//或者
import "fmt"

func main() {
    
    var scores map[string]int
    // 未初始化的 score 的零值为nil,无法直接进行赋值
    if scores == nil {
        // 需要使用 make 函数先对其初始化
        scores = make(map[string]int)
    }
    scores["chinese"] = 90
    fmt.Println(scores)
}
字典的相关操作
获取不存在的value, 不报错,得零值.
scores["math"] = 95
scores["math"] = 100
fmt.Println(scores["math"])
delete(scores, "math")
判断key是否存在
math, ok := scores["math"]

import "fmt"

func main() {
    scores := map[string]int{"english": 80, "chinese": 85}
    if math, ok := scores["math"]; ok {
        fmt.Printf("math 的值是: %d", math)
    } else {
        fmt.Println("math 不存在")
    }
}
遍历字典
key, value
import "fmt"

func main() {
    scores := map[string]int{"english": 80, "chinese": 85}

    for subject, score := range scores {
        fmt.Printf("key: %s, value: %d\n", subject, score)
    }
}
key
import "fmt"

func main() {
    scores := map[string]int{"english": 80, "chinese": 85}

    for subject := range scores {
        fmt.Printf("key: %s\n", subject)
    }
}
value
import "fmt"

func main() {
    scores := map[string]int{"english": 80, "chinese": 85}

    for _, score := range scores {
        fmt.Printf("value: %d\n", score)
    }
}

指针

aint := 1
ptr := &aint
//或者
astr := new(string)
*astr = "Go"
//或者
aint := 1
var bint *int
bint = &aint
指针与切片
切片与指针一样,都是引用类型。
如果我们想通过一个函数改变一个数组的值,有两种方法
  1. 将这个数组的切片做为参数传给函数
  1. 将这个数组的指针做为参数传给函数
尽管二者都可以实现我们的目的,但是按照 Go 语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。
  • 切片
func modify(sls []int) {
    sls[0] = 90
}

func main() {
    a := [3]int{89, 90, 91}
    modify(a[:])
    fmt.Println(a)
}
  • 指针
func modify(arr *[3]int) {
    (*arr)[0] = 90
}

func main() {
    a := [3]int{89, 90, 91}
    modify(&a)
    fmt.Println(a)
}

过程控制

if-else

多个条件可以加括号, 也可以不加
import "fmt"

func main() {
    age := 20
    gender := "male"
    if (age > 18 && gender == "male") {
        fmt.Println("是成年男性")
    }
}
 
import "fmt"

func main() {
    age := 20
if age > 18 {
        fmt.Println("已经成年了")
    }elseif age >12 {
        fmt.Println("已经是青少年了")
    }else {
        fmt.Println("还不是青少年")
    }
}
 
import "fmt"

func main() {
if age := 20;age > 18 {
        fmt.Println("已经成年了")
    }
}

switch-case

不需要break, 没有括号, 穿透需要特别处理.
import "fmt"

func main() {
    month := 2

    switch month {
    case 3, 4, 5:
        fmt.Println("春天")
    case 6, 7, 8:
        fmt.Println("夏天")
    case 9, 10, 11:
        fmt.Println("秋天")
    case 12, 1, 2:
        fmt.Println("冬天")
    default:
        fmt.Println("输入有误...")
    }
}
switch 条件是函数
import "fmt"

// 判断一个同学是否有挂科记录的函数
// 返回值是布尔类型
func getResult(args ...int) bool {
    for _, i := range args {
        if i < 60 {
            return false
        }
    }
    return true
}

func main() {
    chinese := 80
    english := 50
    math := 100

    switch getResult(chinese, english, math) {
    // case 后也必须 是布尔类型
    case true:
        fmt.Println("该同学所有成绩都合格")
    case false:
        fmt.Println("该同学有挂科记录")
    }
}
switch 不接条件, 当if else if else用
score := 30

switch {
    case score >= 95 && score <= 100:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("合格")
    case score >= 0:
        fmt.Println("不合格")
    default:
        fmt.Println("输入有误...")
}
fallthrough 穿透能力, 但是只能穿透一个
package main

import "fmt"

func main() {
	s := "hello"
	switch {
	case s == "hello":
		fmt.Println("hello")
		fallthrough
	case s != "world":
		fmt.Println("world")
		fallthrough // 需要写多个,如果要穿透多个的话
	case s == "world":
		fmt.Println("world")
	}
}

for

没有括号
a := 1
for a <= 5 {
    fmt.Println(a)
    a ++
}

import "fmt"

func main() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i)
    }
}
for range
  • key value
import "fmt"

func main() {
    myarr := [...]string{"world", "python", "go"}
    for _, item := range myarr {
        fmt.Printf("hello, %s\n", item)
    }
}
  • key
import "fmt"

func main() {
    myarr := [...]string{"world", "python", "go"}
    for i := range myarr {
        fmt.Printf("hello, %v\n", i)
    }
}

defer

defer保存了当前快照, 下面程序输出go1 go3 go2
package main

import "fmt"

func main() {
	name := "go1"
	fmt.Println(name)
	name = "go2"
	defer fmt.Println(name)
	name = "go3"
	fmt.Println(name)
}
多个defer 逆序调用
defer在return之后执行.

异常机制:panic 和 recover

panic
package main

func main() {
    panic("crash")
}
recover, 必须在defer中使用
import "fmt"

func set_data(x int) {
    defer func() {
        // recover() 可以将捕获到的panic信息打印
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    // 故意制造数组越界,触发 panic
    var arr [10]int
    arr[x] = 88
}

func main() {
    set_data(20)
    fmt.Println("everything is ok")
}
但是协程内的panic,不会传导给其他协程(包括主协程)

© chaleaoch 2021