20220712 GO语言基础知识
Posted 歆瑶的小房子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20220712 GO语言基础知识相关的知识,希望对你有一定的参考价值。
基础
1. 内置函数
2. 流程控制
2.1. if与switch
2.1.1. if
2.1.2. switch
2.2. for
3. 指针
3.1. 指针地址和指针类型
3.2. 指针取值
3.3. 空指针
4. 数组Array
4.1. 数组定义
4.2. 数组定义
4.3. 与数组相关的函数
5. 切分Slice
5.1. 切分Slice定义
5.2. 切分Slice初始化
5.3. cap与len
5.4. 操作方法
5.4.1. append 元素的添加与删除
5.4.2. copy 切片拷贝
5.4.3. slice遍历
5.5. 其他操作
5.6. 超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满
6. map
6.1. 定义 Map
6.2. map基本使用
6.3. 判断某个键是否存在
6.4. 遍历
6.5. delete()函数删除键值对
6.6. 与切片擦出火花
7. 内置函数
1. 内置函数
append -- 用来追加元素到数组、slice中,返回修改后的数组、slice
close -- 主要用来关闭channel
delete -- 从map中删除key对应的value
panic -- 停止常规的goroutine (panic和recover:用来做错误处理)
recover -- 允许程序定义goroutine的panic动作
real -- 返回complex的实部 (complex、real imag:用于创建和操作复数)
imag -- 返回complex的虚部
make -- 用来分配内存,返回Type本身(只能应用于slice, map, channel)
new -- 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
copy -- 用于复制和连接slice,返回复制的数目
len -- 来求长度,比如string、array、slice、map、channel ,返回长度
print、println -- 底层打印函数,在部署环境中建议使用 fmt 包
2. 流程控制
2.1. if与switch
2.1.1. if
go和php相比,实际上没有特别的区别只是语法上需要注意一下语法即可
• 可省略条件表达式括号。
• 持初始化语句,可定义代码块局部变量。
• 代码块左 括号必须在条件表达式尾部。
if 布尔表达式
/* 在布尔表达式为 true 时执行 */
示例
if n := "abc"; x > 0 // 初始化语句未必就是定义变量, 如 println("init") 也是可以的。
println(n[2])
else if x < 0 // 注意 else if 和 else 左大括号位置。
println(n[1])
else
println(n[0])
*不支持三元操作符(三目运算符) "a > b ? a : b"。
2.1.2. switch
switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
switch var1
case val1:
...
case val2:
...
default:
...
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它
们,例如:case val1, val2, val3。
/* 定义局部变量 */
var grade string = "B"
var marks int = 90
switch marks
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
switch
case grade == "A" :
fmt.Printf("优秀!\\n" )
case grade == "B", grade == "C" :
fmt.Printf("良好\\n" )
case grade == "D" :
fmt.Printf("及格\\n" )
case grade == "F":
fmt.Printf("不及格\\n" )
default:
fmt.Printf("差\\n" )
fmt.Printf("你的等级是 %s\\n", grade )
Type Switch
关于switch也可以用于type检查,switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型
switch x.(type)
case type:
statement(s)
case type:
statement(s)
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s)
例子
package main
import "fmt"
type go_class struct
class_name string
class_stage int
func main()
var g go_class
g.class_name = "shineyork go class"
g.class_stage = 1
sw(10)
sw("s")
sw(g)
func sw(x interface)
switch x.(type) // 带初始化语句
case nil:
fmt.Println(" x 的类型 nil\\r\\n")
case int:
fmt.Println("x 是 int 型")
case float64:
fmt.Println("x 是 float64 型")
case func(int) float64:
fmt.Println("x 是 func(int) 型")
case bool, string:
fmt.Println("x 是 bool 或 string 型")
case go_class:
fmt.Println("x 是 go_class 型")
default:
fmt.Println("未知型")
fallthrough
var k = 0
switch k
case 0:
println("fallthrough")
/*
Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
而如果switch没有表达式,它会匹配true。
Go里面switch默认相当于每个case最后带有break,
匹配成功后不会自动向下执行其他case,而是跳出整个switch,
但是可以使用fallthrough强制执行后面的case代码。
*/
case 1:
fmt.Println("1")
default:
fmt.Println("def")
效果:
D:\\phpStudy\\PHPTutorial\\WWW\\go\\bin>go run shineyork/02/
fallthrough
1
switch不写条件的时候当if与else运用
var n = 0
switch //省略条件表达式,可当 if...else if...else
case n > 0 && n < 10:
fmt.Println("i > 0 and i < 10")
case n > 10 && n < 20:
fmt.Println("i > 10 and i < 20")
default:
fmt.Println("def")
2.2. for
Go语言的For循环有3中形式,只有其中的一种使用分号。
for init; condition; post
for condition
for
init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。
for语句执行过程如下:
①先对表达式 init 赋初值;
②判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就
终止for循环,执行循环体外语句。
例子:
func main()
s := "abc123"
for i, n := 0, length(s); i < n; i++ // 常见的 for 循环,支持初始化语句。
println(s[i])
n := len(s)
for n > 0 // 替代 while (n > 0)
n--
println(s[n]) // 替代 for (; n > 0;)
for // 替代 while (true)
println(s) // 替代 for (;;)
func length(s string) int
println("call length.")
return len(s)
不要期望编译器能理解你的想法,在初始化语句中计算出全部结果是个好主意。
3. 指针
关于go中的指针实际上和PHP的 " & " 功能很是相似,不过还是有一点区别go语言中的指针只需要记住两个符号:&(取地址)和*(根据地址取值)
要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值。
在整之前回顾PHP中的&
在PHP中的&特点就是会引用
<?php
$a = 0;
$b = &$a;
$b++;
echo $a;
?>
输出结果为 1 ;
3.1. 指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类
型,如:*int、*int64、*string等。
取变量指针的语法如下:
ptr := &v // v的类型为T
其中:
v:代表被取地址的变量,类型为T
ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。
例子
var a int = 10
b := &a
fmt.Println(b) // 0xc0000100a0
fmt.Println(a) // 10
fmt.Println(&b) // 0xc000006028
效果:0xc0000100a0
3.2. 指针取值
在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下。
func main()
//指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b中
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of b:%T\\n", b) // type of b:*int
fmt.Printf("type of c:%T\\n", c) // type of c:int
fmt.Printf("value of c:%v\\n", c) // value of c:10
var t1 int = 10
a1 := &t1
*a1 = 100
fmt.Println(t1)
总结: 取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
1. 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
2. 指针变量的值是指针地址。
3. 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
func main()
a := 10
test1(a)
fmt.Println(a)
test2(&a)
fmt.Println(a)
func test1(x int)
x = 0
func test2(a *int)
*a = 0
3.3. 空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil
空指针的判断
var a *int
fmt.Println(a)
4. 数组Array
1. 数组:是同一种数据类型的固定长度的序列。
2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
for i := 0; i < len(a); i++
for index, v := range a
5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
7. 支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
8. 指针数组 [n]*T,数组指针 *[n]T。
4.1. 数组定义
一维数组:
全局的定义方式也适合局部
全局:
var arr0 [5]int = [5]int1, 2, 3
var arr1 = [5]int1, 2, 3, 4, 5
var arr2 = [...]int1, 2, 3, 4, 5, 6
var str = [5]string3: "hello world", 4: "tom"
局部:
a := [3]int1, 2 // 未初始化元素值为 0。
b := [...]int1, 2, 3, 4 // 通过初始化值确定数组长度。
c := [5]int2: 100, 4: 200 // 使用索引号初始化元素。
d := [...]struct
name string
age uint8
"user1", 10, // 可省略元素类型。
"user2", 20, // 别忘了最后一行的逗号。
多维数组:
全局的定义方式也适合局部
全局
var arr0 [5][3]int
var arr1 [2][3]int = [...][3]int1, 2, 3, 7, 8, 9
局部:
a := [2][3]int1, 2, 3, 4, 5, 6
b := [...][2]int1, 1, 2, 2, 3, 3 // 第 2 纬度不能用 "..."。
遍历
var f [2][3]int = [...][3]int1, 2, 3, 7, 8, 9
for k1, v1 := range f
for k2, v2 := range v1
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
fmt.Println()
4.2. 数组定义
形参方式
void myFunction(param [10]int)
void myFunction(param []int)
4.3. 与数组相关的函数
len -- 来求长度,比如string、array、slice、map、channel ,返回长度
实例:
var usernames [5]string = [5]string"shineyork", "cara"
fmt.Println(len(usernames))
5. 切分Slice
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。
1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
2. 切片的长度可以改变,因此,切片是一个可变的数组。
3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
6. 如果 slice == nil,那么 len、cap 结果都等于 0。
5.1. 切分Slice定义
1. 声明一个未指定大小的数组来定义切片
var identifier \\[]type
2. 切片不需要说明长度 或者使用make()函数来创建切片
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中capacity为可选参数
make([]T,length,capacity)
这里的len是数组的长度并且也是切片的初始长度
示例:
//1.声明切片
var s1 []int
// 2.:=
s2 := []int
// 3.make()
var s3 []int = make([]int, 0)
var s4 []int = make([]int, 0, 0)
fmt.Println(s1, s2, s3, s4)
5.2. 切分Slice初始化
// 直接初始化切片,[]表示是切片类型,1,2,3初始化值依次是1,2,3.其cap=len=3
var arr = [...]int0, 1, 2, 3, 4, 5, 6, 7, 8, 9
arr2 := [...]int9, 8, 7, 6, 5, 4, 3, 2, 1, 0
// 初始化切片slice8,是数组arr的引用
var slice3 []int = arr[:]
slice8 := arr[:]
// 将arr中从下标start到end的元素创建为一个新的切片
var slice0 []int = arr[start:end]
slice5 := arr[start:end]
// 默认 end 时将表示一直到arr的最后一个元素
var slice2 []int = arr[start:]
slice7 := arr[start:]
// 默认 start 时将表示一直到arr的最后一个元素
var slice1 []int = arr[:end]
slice6 := arr[:end]
// 通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
s :=make([]int,len,cap)
示例:
arr := [...]int9, 8, 7, 6, 5, 4, 3, 2, 1, 0
slice5 := arr[2:8]
slice6 := arr[0:6] //可以简写为 slice := arr[:end]
slice7 := arr[5:10] //可以简写为 slice := arr[start:]
slice8 := arr[0:len(arr)] //slice := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
fmt.Printf("局部变量: arr %v\\n", arr)
fmt.Printf("局部变量: slice5 %v\\n", slice5)
fmt.Printf("局部变量: slice6 %v\\n", slice6)
fmt.Printf("局部变量: slice7 %v\\n", slice7)
fmt.Printf("局部变量: slice8 %v\\n", slice8)
fmt.Printf("局部变量: slice9 %v\\n", slice9)
值得注意的是读写操作实际目标是底层数组,只需注意索引号的差别。
data := [...]int0, 1, 2, 3, 4, 5
s := data[2:4]
s[0] += 100
s[1] += 200
fmt.Println(s)
fmt.Println(data)
效果
C:\\Users\\shineyork>go run shineyork/02/
[102 203]
[0 1 102 203 4 5]
5.3. cap与len
len -- 来求长度,比如string、array、slice、map、channel ,返回长度
cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
示例:
s1 := []int0, 1, 2, 3, 8: 100 // 通过初始化表达式构造,可使用索引号。
fmt.Println(s1, len(s1), cap(s1))
s2 := make([]int, 6, 8) // 使用 make 创建,指定 len 和 cap 值。
fmt.Println(s2, len(s2), cap(s2))
s3 := make([]int, 6) // 省略 cap,相当于 cap = len。
fmt.Println(s3, len(s3), cap(s3))
效果:
[0 1 2 3 0 0 0 0 100] 9 9
[0 0 0 0 0 0] 6 8
[0 0 0 0 0 0] 6 6
5.4. 操作方法
5.4.1. append 元素的添加与删除 元素的添加与删除
s := []int0, 1, 2, 3
s = append(s, 4)
fmt.Println(s)
i := 2
s = append(s[:i], s[i+1:]...)
fmt.Println(s)
需要注意:append :向 slice 尾部添加数据,返回新的 slice 对象。
s1 := make([]int, 0, 5)
fmt.Printf("%p\\n", &s1)
s2 := append(s1, 1)
fmt.Printf(以上是关于20220712 GO语言基础知识的主要内容,如果未能解决你的问题,请参考以下文章