Go基础
本文最后更新于 1678 天前, 如有失效请评论区留言.
变量和常量
关键字&保留字
# 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. 函数外的每个语句都必须以关键字开始(var、const、func等)
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()
}
- uint8类型, 或者叫 byte型 代表了ASCII码的一个字符.
- rune类型, 代表一个 UTF-8字符(中文), 实际是一个int32.
- 因为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,map, chan申请内存,返回类型本身
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)
闭包
- 函数作为返回值
- 外部变量引用
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 打印类型