跳到主要内容

Go基础

· 阅读需 9 分钟

变量和常量

关键字&保留字

# 25个保留字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

# 37个保留字

Constants: true false iota nil

Types: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error

Functions: make len cap new append copy close delete
complex real imag
panic recover

注意

1. 函数外的每个语句都必须以关键字开始(varconstfunc等)
2. :=不能使用在函数外。
3. _多用于占位,表示忽略值(匿名变量,不占用命名空间,不会分配内存,不需要存在重复申明)

变量

package main

import (
"fmt"
)
// 全局变量m
var m = 100

func main() {
n := 10
m := 200 // 此处声明局部变量m
fmt.Println(m, n)
}

常量

常量是恒定不变的值

const pi = 3.1415

const (
// const声明多个常量时,如果省略了值则表示和上面一行的值相同
p1 = 3.1415
p2 // 3.1415
p3 // 3.1415
)

iota

iota是go语言的常量计数器,只能在常量的表达式中使用。iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)

const (
n1 = iota // 0
n2 // 1
n3 // 2
_ // 跳过某些值
n4 // 3
n5 = 10 // 10
n6 // 4
)

const (
_ = iota // 0
KB = 1 << (10 * iota) // 1 1<<10 2^10 1024
MB = 1 << (10 * iota) // 2 1<<20 2^20 1024*1024
)

const (
a, b = iota + 1, iota + 2 // 1,2
c, d // 2, 3
)

基本数据类型

package main

import (
"fmt"
"math"
)

func main() {
var a int = 17 // int 型 (int64)
fmt.Printf("%d \n", a) // 17 十进制
fmt.Printf("%b \n", a) // 10001 二进制
fmt.Printf("%o \n", a) // 021 八进制
fmt.Printf("%x \n", a) // 0x11 16禁止

fmt.Printf("%.2f \n", math.Pi) // 默认都是浮点型float64

var s = "666"
fmt.Printf("%T \n", s) // 类型 string
fmt.Printf("%v \n", s) // 值 666
fmt.Printf("%#v \n", s) // 值 "666"
}

注:

布尔类型变量的默认值为false
Go 语言中不允许将整型强制转换为布尔型.
布尔型无法参与数值运算,也无法与其他类型进行转换

字符串

字符串需要使用"

s1 := "ysicing"

多行字符串需要使用```

s1 := `牛
牛 牛

`

字符串操作

package main

import (
"fmt"
"strings"
)

func main() {
a := "666"
b := "777"
c := a + "-" + b
fmt.Println(len(a)) // len 长度
fmt.Println(fmt.Sprintf("%s%s", a, b)) // 拼接
fmt.Println(a + b) // 拼接
fmt.Print(strings.Split(c, "-")[0]) // 分割
res := strings.Split(c, "-") // 分割
fmt.Println(res)

fmt.Println(strings.HasPrefix(c, "6"), strings.HasSuffix(c, "6")) //前缀/后缀判断

s := "China万岁"
for i := 0; i < len(s); i++ { // byte
fmt.Printf("%T %v(%c) \n", s[i], s[i], s[i])
}
fmt.Println()
for _, j := range s { // rune
fmt.Printf("%T %v(%c) \n", j, j, j)
}
fmt.Println()
}
  1. uint8类型, 或者叫 byte型 代表了ASCII码的一个字符.
  2. rune类型, 代表一个 UTF-8字符(中文), 实际是一个int32.
  3. 因为UTF8编码下一个中文汉字由3~4个字节组成,一个rune字符由一个或多个byte组成.

流程控制

if

if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}

for

package main

import "fmt"

func main() {
// 基本格式
for i := 1; i <= 3; i++ {
fmt.Println(i)
}

j := 1
for ; j <= 3; j++ {
fmt.Println(j)
}

k := 1
for k <= 3 {
fmt.Println(k)
k++
}

s := "HellGo"
for i, j := range s {
fmt.Printf("%d--->%v(%T)-->%c\n", i, j, j, j) // rune
}

// 死循环
//for {
// fmt.Println("666")
//}
}

switch case

简化判断

a := 4
switch a {
case a < 1:
fmt.Println("1")
case 2,3:
fmt.Println("2")
default:
fmt.Println("3") // 3
}

b := 3
switch a {
case a < 1:
fmt.Println("1")
case 2,3:
fmt.Println("2")
fallthrough // 可以执行满足条件的case的下一个case, 了解即可
default:
fmt.Println("3")
}

// 23

goto

func main() {
for i := 1; i <= 9999; i++ {
for j := 3000; j <= i; j++ {
if j < i {
fmt.Printf("%d * %d = %d ", i, j, i*j)
} else if j == i {
fmt.Printf("%d * %d = %d\n", i, j, i*j)
goto breakTag
}
}
}

breakTag: // label
fmt.Println("end")
}

运算符

算数运算符: + - * / % 关系运算符: == != > >= < << (结果为bool值) 逻辑运算符: && || ! (结果为bool值) 位运算符: & (相与 同为1为1)|(相或 有1为1) ^(相异 或不同为1) << (乘2的n次方) >> (除2的n次方) 赋值运算符: 先其他运算再赋值

5 << 2 // 5 --- 101 左移 2 10100 --- 10 5 * 2^ 1

5 >> 2 // 5 --- 101 右移 2 001 --- 1

注: ++,-- 单独语句,不是运算符

数组

var 数组变量名 [元素数量]T // 一旦定义,长度不能变
var a [3]int

数组初始化

var a1 [3]int //数组会初始化为int类型的零值
var a2 = [3]int{1,2} //使用指定的初始值完成初始化
var a3 = [...]int{1, 2} // 让编译器根据初始值的个数自行推断数组的长度
a4 := [...]int{1: 1, 3: 5} // 指定索引值的方式来初始化数组 [0, 1, 0 ,3]

数组遍历

package main

import "fmt"

func main() {
var a = [...]int{1, 2, 3, 4, 5}
// for循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// range
for i, j := range a {
fmt.Println(i, j)
}

b := [...][2]int{
{1,2},
{2,1},
}

for _, i := range b {
for _, j := range i {
fmt.Printf("%d\t", j)
}
}
}

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。

切片slice

切片是引用类型,拥有相同类型元素的可变长度的序列

var 切片名 []切片类型
var name []string
# 动态创建
make([]T, size, cap) // T 类型,size 长度, cap容量(从第一个到最后容量数),cap 可省却
package main

import "fmt"

func main() {
//var a []int
var c = []string{"a", "b"}
fmt.Println(len(c), cap(c))
d := make([]int, 2, 10)
fmt.Println(len(d), cap(d))
e := d[3:6]
fmt.Println(len(e), cap(e)) // 3, 3-10 7


s1 := make([]int, 3) // [0,0,0]
s2 := s1
// 拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容
s2[0] = 100
fmt.Println(s1)
fmt.Println(s2)

// 遍历和for一样
for i := 0; i < len(s1); i++ {
fmt.Printf("%d\t", s1[i])
}
for _, i := range s2 {
fmt.Printf("%d\t", i)
}
}

切片的本质就是对底层数组的封装

append

package main

import "fmt"

func main() {
var num []int
for i := 0; i < 20; i++ {
// 追加一个元素
num = append(num, i)
fmt.Printf("%v len:%d cap:%d ptr:%p\n", num, len(num), cap(num), num)
}
// 追加多个元素
num = append(num, 1, 2, 3)
fmt.Printf("%v, \n", num)
num1 := []int{99, 98}
// 追加切片
num = append(num, num1...)
fmt.Println(num)
}

copy

copy(目标切片,源切片)
func main() {
// copy()复制切片
a := []int{1, 2, 3, 4, 5}
c := make([]int, 5, 5)
copy(c, a) //使用copy()函数将切片a中的元素复制到切片c
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1 2 3 4 5]
c[0] = 1000
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1000 2 3 4 5]

var a = make([]string, 5, 10) // 已经有5个了
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i))
}
fmt.Println(a, cap(a), len(a))

// [ 0 1 2 3 4 5 6 7 8 9] 20 15
}

指针

  • 取地址 &a
  • 根据地址取值 *b

make & new

都用来申请内存

make: slice,mapchan申请内存,返回类型本身
new: 基本数据类型申请内存, 返回指针, 且内存对应的值为类型零值

a := new(int)

map

散列表(hash)实现, 无序的基于key-value的数据结构,引用类型,必须初始化才能使用.

map[KeyType]ValueType

make(map[keytype]valuetype, [cap])

code := make(map[string]string, 10)

code["a"] = "a"
code["b"] = "b"

# 判断 值是否存在
value, ok := map[key]
if ok {
// 存在
} else {
// 不存在
}

// 循环
for k, v := range map {
// k,v v可省略
}

函数

func 函数名(参数)(返回值){

}

示例

func sum1(x, y int) int {
sum := x + y
return sum
}

// 返回值定义了返回值名,return可省却
func sum2(x, y int) (sum int) {
sum = x + y
return
}

// 可变参数,可变参数可有可无
func sum3(x int, y ...int) {
fmt.Println(x)
fmt.Println(y) // 切片
}

func main() {
r1 := sum1(1, 2)
r2 := sum2(1, 2)
fmt.Println(r1, r2)
sum3(1) // 1 []
sum3(1, 2) 1 [2]
sum3(1, 2, 3) 1 [2,3]
}

defer

延迟处理, 多个defer 按照先进后出处理。

函数return分成两步

  • 返回值赋值
  • defer
  • return
package main

import "fmt"

func deferdemo(name string) {
fmt.Println(name)
}

func f1() int {
x := 5
defer func() {
x++ // 修改x不是 返回值
}()
// 返回值赋值 5
// 修改x值为 6
// retrun 5
return x
}

func f2() (x int) {
defer func() {
x++
}()

// 返回值赋值 x=5
// 修改x值为 x=6
// return 6

return 5 // 返回值
}

func f3() (y int) {
x := 5
defer func() {
x++
}()

// y = x = 5
// x = 6
// return y = 6

return x
}

func f4() (x int) {
defer func(x int) {
x++
}(x)
// x = 5
// 副本 x = 6
// return x
return 5
}

func main() {
deferdemo("666")
defer deferdemo("777")
deferdemo("888")
defer deferdemo("999")

fmt.Println(f1(), f2(), f3(), f4()) // 5,6,5,5

}

匿名函数


// 多次执行
e1 := func(x, y int) {
fmt.Println(x)
fmt.Println(y)
fmt.Println(x + y)
}
e1(2, 4)

// 立即执行,只执行一次
func(x, y int) {
fmt.Println(x * y)
}(1, 2)

闭包

  1. 函数作为返回值
  2. 外部变量引用
package main

import "strings"

import "fmt"

// checkMail
func checkMail(domain string) func(string) string {
return func(name string) string {
if strings.HasSuffix(name, domain) {
return name
}
return name + "@" + domain

}
}

func main() {
talkFunc := checkMail("@ysicing.me")
fmt.Println(talkFunc("i"))
fmt.Println(talkFunc("root@ysicing.me"))
}

panic/recover

  • recover()和defer一起使用
  • defer在panic语句前
package main

import "fmt"

func funcA() {
fmt.Println("a")
}

func funcB() {

defer func() {
err := recover()
fmt.Println(err)
}()
panic("error")
fmt.Println("b")
}

func funC() {
fmt.Println("c")
}

func main() {
funcA()
funcB()
funC()
}

占位符

    m1 := make(map[string]int, 5)
m1["demo"] = 1
fmt.Printf("%v\n", m1) // 值的默认格式
fmt.Printf("%+v\n", m1) // 类似%v, 结构体会加字段名
fmt.Printf("%#v\n", m1) // map[string]int{"demo":1} 值Go语法
fmt.Printf("%T\n", m1) // map[string]int 打印类型

结构体

标签: