GO基础
Golang内置类型和函数
1.1 内置类型
1.1.1 值类型
bool
int8,int16,int32,int64
uint8,uint16,uint32,uint64
float32,float64
string
array ---固定长度的数组
1.1.2 引用类型
slice --- 序列数组
map --- 映射
chan --- 管道
1.2 内置函数
append -- 用来追加元素到数组、slice中,返回修改后的数组
close -- 用来关闭channel
delete -- 从map中删除key对应的value
panic -- 停止常规的goroutine(panic和recover:用来做错误处理)
recover -- 允许程序定义goroutine的panic动作
real -- 返回complex的实部
imag -- 返回complex的虚部
make -- 用来分配内存,返回Type本身(只能应用于slice,map,channel)
new -- 用来分配内存,主要用来分配值类型,比如int、struct。返回指向type的指针
cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和map)
copy -- 用于复制和连接slice,返回复制的数目
len -- 长度,
print、println -- 底层打印函数,在部署环境中建议使用fmt包
运算符
GO语言内置的运算符有:
- 算术运算符:“+”、“-”、“*”、“/”、“%”
- 关系运算符:“==”、“!=”、“>”、“>=”、“<”、“<=”
- 逻辑运算符: “&&”(如果两边的操作都是true,则为true,否则为false)、“||”(如果两边的操作数有一个true,则为true,否则为false)、“!”
- 位运算符(下图1)
- 赋值运算符(下图2)
变量和常量
const 同时声明多个常量时,如果省略了值则表示和上面一行的值相同
func main(){
const (
a = 100
b
c
)
fmt.println("c=",c)
}
结果:b=100,c=100
数组
1、概念:是同一种数据类型的固定长度的序列;
2、定义:var a [len]int ⇒ var a [3]int;数组长度不能变
3、访问越界:如果下标在数组合法范围外,则触发访问越界,会panic
4、数组是值类型,赋值和传参会赋值整个数组,而不是指针。因此改变副本的值,不会改变本身的值;
切片Slice
一、概念
1、概念:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递;
2、切片的长度是可以改变的,因此,切片是一个可变的数组;
3、定义:var a []类型 ====》var a []string
4、slice并不是数组或者数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案
5、当声明一个数组时,如果不指定该数组的长度,则该类型为切片
6、如果slice == nil,那么len、cap结果都等于0
7、cap可以求出slice最大扩张容量,不能找过数组限制;0<=len(slice)<=len(array),array是slice引用的数组
8、切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装。
切片Slice源码的数据结构
type slice struct{
array unsafe.Pointer//指向数组的指针
len int //当前切片的长度
cap int //当前你切片的容量。cap总是大于len的
}
二、声明方式
1、未指定大小的数组来定义切片;
定义切片 var identifity []int
或者简单定义:identifity := []int{}
定义数组 var identifity [5]int
[]没有声明长度说明这是一个切片,而不是一个数组,数组声明是必须指定长度的
这种方式只声明不初始化,默认为nil(没有分配内存):len=0,cap=0,slice=[]
2、声明一个拥有初始长度或规定容量的切片,使用make()函数来创建切片
var slice []int = make([]int,len,cap)
或者 slice := make([]int,len,cap)
len是数组的长度也是切片的初始长度
cap为可选参数,如果cap不写,则默认cap=len
这种方式创建切片,切片被系统自动初始化为0,不是nil。make函数为其分配了内存空间
三、切片初始化
1、声明的时候同时初始化
s := []int{1,2,3}
2、用数组初始化切片
array := [5]int{0, 2, 3, 4}
slice := array[:3]
fmt.Println("slice=", slice)
fmt.Println("array=", array)
fmt.Printf("len(slice)=%v,cap(slice)=%v", len(slice), cap(slice))
slice[6:8] 表示下标从第6位到第7位,len=2,cap=4
slice[:6:8] 表示slice内容是从-5,len为6 ,cap为8
slice[6:8:8] 表示slice内容为第6位-第8位,len=8-6,cap=8-6
3、切片的内存布局
读写操作目标还是底层数组切片本身就是一个引用
data := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := data[1:4] //[2,3,4]
slice[0] += 10
slice[1] += 20
fmt.Println("slice = ", slice)
fmt.Println("data = ", data)
结果:
slice = [12 23 4]
data = [1 12 23 4 5 6 7 8 9]
对切片内容的改变实际上改变的是所引用的数组
五、append()和copy()
1、append()实现切片追加
var numbers []int = make([]int, 3, 5)
fmt.Printf("numbers=%v,len(numbers)=%v,cap(numbers)=%v\n", numbers, len(numbers), cap(numbers))
num := append(numbers, 1, 2)
fmt.Printf("num=%v,len(num)=%v,cap(num)=%v\n", num, len(num), cap(num))
num[2] = 200
fmt.Printf("num=%v,len(num)=%v,cap(num)=%v\n", num, len(num), cap(num))
num1 := append(numbers, 1, 2, 3)
fmt.Printf("num1=%v,len(num1)=%v,cap(num1)=%v\n", num1, len(num1), cap(num1))
num1[2] = 100
fmt.Printf("num1=%v,len(num1)=%v,cap(num1)=%v\n", num1, len(num1), cap(num1))
fmt.Printf("numbers=%v,len(numbers)=%v,cap(numbers)=%v\n", numbers, len(numbers), cap(numbers))
结果
numbers=[0 0 0],len(numbers)=3,cap(numbers)=5
num=[0 0 0 1 2],len(num)=5,cap(num)=5
num=[0 0 200 1 2],len(num)=5,cap(num)=5
num1=[0 0 200 1 2 3],len(num1)=6,cap(num1)=10//超出了数组容量,切片扩容1倍
num1=[0 0 100 1 2 3],len(num1)=6,cap(num1)=10 //新的底层数组
numbers=[0 0 200],len(numbers)=3,cap(numbers)=5//append超出切片容量之后,会重新分配底层数组,修改值不会在影响原始的numbers本身,
2、copy()切片的拷贝
浅拷贝:源切片和目的切片共享同一底层数组空间,源切片修改,目的切片同样被修改
深拷贝:源切片和目的切片各自都有彼此独立的底层数组空间,各自的修改,彼此不受影响
浅拷贝
slice1 := make([]int, 5, 5)
slice2 := slice1
slice1[1] = 1
fmt.Println(slice1)
fmt.Println(slice2)
结果:
[0 1 0 0 0]
[0 1 0 0 0]
深拷贝
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("data = ", data)
s1 := data[8:]
s2 := data[:5]
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
copy(s1, s2)
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
fmt.Println("data=", data)
结果:
data = [0 1 2 3 4 5 6 7 8 9]
s1= [8 9]
s2= [0 1 2 3 4]
s1= [0 1]
s2= [0 1 2 3 4]
data= [0 1 2 3 4 5 6 7 0 1]
六、GO切片扩容策略
如果切片的容量小于1024个元素,于是扩容的时候就翻倍增加容量。
一旦元素个数超过1024个元素,那么增长因子就变成1.25,即每次增加原来的四分之一;
注:扩容扩大的容量都是针对原来的容量而言的,而不是针对原来的数组长度而言的;
七、指针
1、&(取地址)和*(根据地址取值)
2、
对变量进行取地址(&)操作,可以得到这个变量的指针变量;
指针变量的值是指针地址
对指针变量进行取值(*)操作,可以获得指针变量指向原变量的值;
a := 10
b := &a
fmt.Printf("a的地址%p,type为%T\n", &a, a)
fmt.Printf("b的地址%p,type为%T,b的值为%v", b, b, *b)
结果:
a的地址0xc0000aa058,type为int
b的地址0xc0000aa058,type为*int,b的值为10
3、空指针
当一个指针被定义后没有分配到任何变量时,值为nil;
var p *string
fmt.Printf("p的值是%v\n", p)
结果:
p的值是<nil>
八、Map
1、概念:Map是一种无序的基于key-value的数据结构,Go语言中的map类型是引用类型,必须初始化才能使用
2、定义:map[keyType]valueType
用make函数来分配内存:make(map[keyType]valueType,cap) ====》map(map[string]string,8)
3、 初始化
1、 make()初始化
scoreMap := make(map[string]string, 8)
scoreMap["小名"] = "90"
scoreMap["hha"] = "30"
fmt.Println(scoreMap)
fmt.Printf("score的type为%T", scoreMap)
2、也可以在声明时填充数据
userMap := map[string]string{
"username": "duanjiaqi",
"age": "18",
}
fmt.Println(userMap)
4、遍历map
scoreMap := make(map[string]string)
scoreMap["xiaoming"] = "90"
scoreMap["xioabai"] = "30"
for k, v := range scoreMap {
fmt.Println(k, v)
}
结果:
xiaoming 90
xioabai 30
5、delete()删除键值对
delete(map,key) map:要删除键值对的map;key:要删除键值对的键名
scoreMap := make(map[string]string)
scoreMap["xiaoming"] = "90"
scoreMap["xioabai"] = "30"
delete(scoreMap, "xiaoming")
for k, v := range scoreMap {
fmt.Println(k, v)
}
结果:
xioabai 30
九、结构体
1、定义方式
type 类型名 struct{
字段名 字段类型 //结构体中的字段名必须是唯一的
字段名 字段类型
...
}
2、结构体实例化
var 结构体实例 结构体类型
type person struct {
name string
city string
age int
}
var p person
p.name = "hah"
p.city = "洪洞"
p.age = 18
fmt.Println(p)
fmt.Println(p.name)
fmt.Printf("p的值为%#v", p)
结果:
{hah 洪洞 18}
hah
p的值为main.person{name:"hah", city:"洪洞", age:18}