目录
Golang
变量与常量
变量声明后必须调用,否则会报错、
:= 和 =的区别
“=”仅仅是赋值
“:=”是首次赋值,同时会完成定义
常量生成器:iota
iota可以通过枚举创建一系列相关的值,并且不需要明确定义类型
iota每次从0开始取值,逐次加1
一、基础要素
Printf格式化输出的通用占位符
%v | 值的默认格式 |
---|---|
%+v | 添加字段名(只要用于结构体struct) |
%#v | 相应值的Go语法表示 |
%T | 相应值数据类型的Go语法表示(如string) |
%% | 字面上的百分号%,并非值的占位符 |
%s | 字符串 |
%d | 数字以十进制表示 |
%f | 浮点数形式 |
%e | 科学计数法的形式 |
%b | 字符的二进制表示,可以是字符('a'),也可以是数字(97) |
%x | 数字以十六进制表示,字符串则打印每一个字符的ASCII码 |
%t | 布尔值表示 |
%c | 字符对应的ASCII码 |
%p | 输出指针(内存地址)的值,例如0x72f51790a |
派生类型
指针类型(pointer)
数组类型(array)
结构类型(struct)
信道类型(channel)
函数类型(func)
切片类型(slice)
接口类型(interface)
字典类型(map)
运算符
Go语言的自增运算++和自减运算符--,它们不能出现在表达式的末尾,即数值的自增和自减不能赋值给其他变量。
整数类型和浮点类型的变量不能直接进行四则运算和进行比较,否则会报类型不匹配的错误
逻辑运算符
非(!)
与(&&)
或(||)
&&和||为双目运算符,两边表达式的结果值必须为true或false,逻辑非总是取后缀值的相反值
位运算符
按位与(&)
按位或(|)
按位异或(^)
左移(<<)
右移(>>)
特殊运算符
& 是取地址运算符
*是取地址所对应的值的运算符
类型转换
将类型A的值转换成类型B的值的语法:
类型B(类型A的值)
二、函数
函数基本语法
func 函数名([参数列表]) [函数返回值类型]{
函数体
}
获取用户终端输入
fmt.Scanln(&变量)
fmt.Scanf("格式",&变量)
三、流通控制
if基本语法:
if 条件1{
满足条件1要执行的程序语句
}else if 条件2{
满足条件2要执行的程序语句
}else{
条件都不满足时要执行的查询语句
}
switch基本语法:
switch 表达式{
case 目标值1:
处理1
case 目标值2:
处理2
default:
默认处理
}
case后面可以待多个表达式,使用逗号间隔。
switch条件判断语句中的表达式可以省略,相当于退化为if...else...条件判断语句。
fallthrough穿透
switch条件判断语句,由于在默认情况下每个case分支都自带break,因此一旦成功匹配后就不会再执行其他case分支。在某些情况下,我们可能需要继续执行后面case,就需要使用fallthrough关键字进行穿透。
for循环语句
1)基本语法
for 给循环变量赋初值的表达式; 循环条件表达式; 循环变量自增或自减少的表达式{
循环体内要执行的程序语句
}
2)相当于while语法
for 循环条件表达式{
循环体内要执行的程序语句
}
3)实现次数不确定循环的语法
for{
循环体内要执行的程序语句
}
break与continue
break语句可用于跳出循环,并退出所在的循环体。
continue语句可用于在出现指定的条件时中断循环中当前的迭代,然后继续循环中的下一个迭代,但并没有退出当前的循环体。
4.错误处理机制
在Go语言中,错误或异常有error和panic两种,
error一般是程序开发者预知的,会进行合适的处理,如监测用户的输入不合法,抛出一个自定义的error错误。
panic是程序开发者无法预知的异常,如引用的对象值为空。
error
两种创建error方法:
error.New
fmt.Errorf
panic
一般没有recover的情况下panic会导致程序崩溃,然后Go运行时会打印出调用栈。在Go语言中,panic、defer和recover经常一起出现,用于异常处理。
panic在抛出它的函数中向自己和所有上层逐级抛出,如果到最顶层还没有recover将其捕获,那么程序就会崩溃;如果在其中某一层defer注册的函数中被recover捕获到,被注册的函数就将获得程序控制群,从而进行异常的善后处理。
5.复合数据类型
type关键字
通过type进行自定义数据类型。通过type关键字对整数类型、浮点类型、布尔类型和字符串类型等基本数据类型重新定义出一种新的数据类型。
type关键字定义的新数据类型可以基于Go语言内置的基本数据类型,也可以基于struct类型来定义。
type 新数据类型 基于的数据类型
type关键字还可以定义一个类型的别名
type 别名 = 数据类型
struct类型
结构体是一系列不同或相同数据类型的数据构成的数据集合
声明struct类型的语法:
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
...
字段n 字段n类型
}
struct可以作为函数的参数进行传递。
struct对象作为函数参数时,会复制一个副本进行值传递,因此在函数体中虽然修改了struct参数的属性值,但原始的struct对象并不会受影响
struct的嵌套可以是多层的,不限于2层,但Go语言中的struct本身不能嵌套自己(可以是自己的指针类型),否则会报递归类型错误。
匿名struct类型
定义匿名struct时没有type关键字,与定义普通类型的变量一样,如果是在函数外定义匿名结构体变量,则需在结构体变量前加var关键字,但在函数内部可省略var关键字
var 变量名 struct {
字段1 字段1类型
字段2 字段2类型
...
字段n 字段n类型
}
匿名struct一般用作全局的配置数据存储结构,如存储数据库链接到的用户名信息、用户密码信息和链接地址信息等。
数组
数组是一组同类型的数据集合,数组的个数往往是确定的,在内存中也是连续存储,因此数组的存取效率很高。
数组元素的类型可以是内置类型,比如字符串、整型、布尔类型等,也可以是自定义的类型
var 数组名 [数组长度] 数据类型
数组一旦定义后,每个元素会自动初始化为零值。
var arr[5] int
//数组支持用短变量的方式进行定义,在定义数组时完成特定的初始化工作
arr2 := [5] int{7,5,4,3,2}
//数组可以进行赋值,但两个数组的类型和长度必须一致
arr3 :=arr2
数组是由长度和元素类型共同组成,只有当两个数组变量和元素类型都一致时,才能进行相等比较,否则判断会报类型不匹配的错误。满足条件:
1)长度一样
2)元素类型一致
3)各元素的值一致
数组可以作为函数的参数进行传递
如果定义一个整数类型的变量length,然后用这个length作为数组长度,编译器会报错。因为只有常数才能用来定义数组的长度。
//以下这种用法会报错
length := 100
var arr [length] int
二维数组
二维数组本质上是又一维数组构成的
var 数组名 [行长度] [列长度] 数据类型
切片
切片的长度可以动态扩展,并且切片在函数中作为参数时,只需要复制一些标志头信息即可,不需要复制底层的数组数据,效率高,占用内存少
切片不需要显式指定长度,当用切片作为函数参数时,函数不会检测切片的长度,只要切片的类型一致,则函数就可以对该切片参数进行处理。
切片的组成部分:
-
指向底层数组的指针
-
长度
-
容量
定义切片语法:
var 切片名 [] 数据类型
切片值定义的话是无法使用的,还需要make内置函数进行初始化才能进行数据的存取。
通过make函数定义和初始化更简洁的语法如下:
切片名 := make([]数据类型,长度,容量)
切片的长度是'指当前元素的个数,容量是指最大存储空间的个数,容量必须比长度大,否则会报错
如果省略容量,则切片的容量与长度一致。
切片的赋值是共享底层数组,只需复制切片的表示头部信息即可
【所以通过切片创建新的切片后,修改新的切片,原来切片的值也会随之改变】
通过切片创建新的切片的基本语法:
slice[i:j] //从slice下标为i(i从0开始)的元素开始切,切片长度为j-i
slice[i:j:k] //从slice下标为i(i从0开始)的元素开始切,切片长度为j-i,容量为k-i
slice[i:] //从slice下标为i(i从0开始)的元素开始切到最后一个
slice[:j] //从slice下标为0的元素开始切到j-1的元素,不包含j
slice[:] //从头切到尾,等同于slice复制
切片的扩容
切片除了可以通过切分缩小切片,也可以通过切片的动态增长实现切片的扩容。
切片的扩容通过内置append函数来实现,这个函数能够快速且高效地对切片进行扩容操作。
内置的append函数有两个参数:第一个参数是将被处理的切片,另一个参数是要追加的值。append函数会返回一个扩容后的新切片
切片作为函数的参数
切片如果作为函数的参数,则函数内部的切片参数和外部的切片参数实际上的底层数组是同一个对象。因此函数内部修改切片的值会影响外部的参数切片值。
切片作为一个比数组更加实用且高效的数据结构,可以实现更加通用的函数。
与数组类似,切片可以是多维的。
字典
字典是无序的数据结构,因此无法确定字典的返回顺序。本质上字典使用哈希函数来实现内部的映射关系。
定义字典的语法:
var 字典名 map[key 数据类型]值数据类型
与切片一样,字典只通过var来定义是无法直接使用的,必须通过make进行初始化才能使用。一旦为字典分配内存后,就可以对多个键进行赋值操作。
可以调用delete函数删除字典中已有的项
字典作为函数的参数
字典作为函数参数值时,底层是共享的内存结构,因此函数体内对字典的修改也会反映到外部字典中。
range关键字
通过range关键字实现迭代操作。range关键字可迭代数组、切片和字典等数据结构。在数组和切片中它返回元素的索引和索引对应的值。
四、指针
指针概述
指针本质上是内存地址,一般为内存中存储的变量值的起始位置。指针变量即存储内存地址的变量。指针是对内存数据的一种引用。
指针的优势在于:使用指向数据的指针作为函数的函数,能把大量的数据作为函数参数进行传递,速度更快,内存占用更低。
指针代表了一个变量的地址和类型
Go语言中提供了指针的功能,不能直接对指针进行偏移运算,但是可以通过非安全的方式(unsafe包)进行指针偏移运算。这样既可以更好地发挥指针的优势,也能避免C语言中指针过于灵活而可能导致的诸多问题。
Go语言允许开发人员控制特定数据分配内存空间的数量以及内存访问的模式。
Go语言的程序在启动时会从操作系统申请一大块内存作为内存池,由Go内存分配器对这块内存进行二次分配,以防止频繁的系统内存申请操作,以提高效率。回收对象内存时,并没有将其真正释放掉,而是放回预先分配的大块内存中,以便复用。
获取变量的内存地址(取地址操作)基本语法:
&变量名
取地址操作符&与C语言中的取地址操作符一样,变量定义后可以通过操作符&来取地址操作
Go变量的地址分配不是连续的
不能对一个const常量或各类型的字面量取地址,比如&3;也不能取地址的地址,如:&(&a)
声明指针
声明一个变量的指针的基本语法:
var 指针名 *类型 = 初始化值
在声明中,*表示指向某个类型的指针。
var a int = 7
var p *int = &a
指针变量p保存了变量a的内存地址
指针可以让不同的变量共享同一块内存空间。
关键字new
通过关键字new创建指针类型
指针名 := new(类型)
new关键字配合短变量定义方式可以声明一个指针类型,并初始化为对应类型的零值。
Go语言除了通过关键字new分配内存外,还可以 通过make关键字分配内存。make仅适用于map、slice和channel,且返回的不是指针。new返回的是指针
unsafe包
一般情况下,不同类型的指针是不允许相互赋值的,但可以通过unsafe包对不同类型的指针进行转换。
野指针
野指针是指一种指向的内存位置是不可预知的指针,一般是由于指针变量在声明时未初始化所导致的。