基础数据类型与变量
基本数据类型
go语言提供了丰富的数据类型
整形
byte,int,int8,int16,int32,int64,还有uint等
浮点
float32,float64
字符串
string
rune
rune等同于int32,这里提一个用法,统计非unicode字符数,用rune
类型转换
go不支持隐式类型转换,必须显示指定,类似函数,valueOfTypeB = typeB(valueOfTypeA), demo
a := 5.0
b := int(a)
变量
变量的定义相对灵活,go语言的一个特性是对变量类型的自动推导,看例子。
普通声明
普通声明使用var,变量名在前类型在后
// var 声明
var value1 int = 123
// 或者
var value2 int
value2 = 123
自动推导类型
可以省略变量的类型,由go自动推导
var value3 = 123
fmt.Printf("value3 type %T\n", value3) // 推导为int
var value4 = 3.14
fmt.Printf("value4 type %T\n", value4) // 推导为float64
var value5 = 's'
fmt.Printf("value5 type %T\n", value5) // 推导为int32
var value6 = "hello"
fmt.Printf("value6 type %T\n", value6) // 推导为string
var value7 = intValue() // intValue函数返回int值
fmt.Printf("value7 type %T\n", value7) //推导为int
冒号简写
上面的类型推导可以采用冒号简写
value8 := 234
value9 := 3.14
value10 := "string"
value11 := intValue()
多个变量同时定义
var a1, b1, c1 int = 1, 2, 3
var a2, b2, c2 = 4, 5, 6
a3, b3, c3 := 7, 8, 9
集中声明
多个变量建议集中声明
var (
ivalue int
fvalue float64
svalue string
)
ivalue, fvalue, svalue = 234, 3.1415, "abc"
匿名变量
匿名变量用来对数据做丢弃处理,例如一个函数返回了两个值a和b,只需要a,如果同时声明了b没用到,编译会报错,这时候可以用匿名变量来代替b,即下划线_
// 匿名变量
first, second := intValue2() // intValue2 有两个返回值
_, value := intValue2() // 丢弃第一个返回值不处理
key, _ := intValue2() // 丢弃第二个返回值不处理
常量
常量采用const声明
const STR string = "const" // 指定类型
const PI = 3.1415 // 自动推导类型
const VALUE = 2333 // 自动推导类型
常量集中声明与iota
// iota 自增常量,从0开始
// 集中声明
const (
a = iota // a=0
b, c, d = iota, iota, iota // 1,1,1 换行才会自增
e = iota // 2
)
const ( // 出现const则iota重置为0
f = iota
g // 省略复制,go语言可自动复制,g=1
_ // 跳过值,跳过2
h // h = 3
)
复合数据类型
复合数据类型有指针、数组、切片、map、通道、结构体、接口,这里涉及指针、数组、切片、map,其他的在后面
指针
go的指针与c的指针类似,*来声明指针,&来对指针赋值,取值则使用星号*
value := 123
var intp *int // *声明为指针类型
intp = &value // *声明变量为指针类型,
fmt.Println(*intp) // *取值
其他类型的指针会随 对应的类型说明
数组
数组是定长不可变的,长度在声明时需确定,声明方式有以下几种:
var arr1 [3]int // 显示声明,指定长度,不指定元素值,默认值为0
arr2 := [4]int{1,2,3} // 自动推导类型,指定长度,指定部分元素值,未指定的默认值为0
arr3 := [...]int{1,2,3,4} // 自动推导类型,中括号的...表示不指定长度,由后面元素推导
arr4 := [...]int{1:5, 4:6} // 自动推导类型,自动推导长度,1:5表示下标为1的元素值为5,未指定的默认值为0
fmt.Println(len(arr4)) // len获取数组长度
// 数组指针
var arrPtr1 *[4]int = &arr2
arrPtr2 := &arr4
// 数组赋值
arr1[1] = 6
数组的长度与遍历
主要有两种遍历,使用下标遍历和使用range函数遍历。
下标遍历需用len()获取数组长度
// 数组遍历,下标遍历
for i := 0; i < len(arr4); i ++ { // len获取长度
fmt.Println(arr4[i])
}
range()函数提供遍历功能,返回两个值,第一个为下标,第二个为位置上的值
// range 遍历
arr5 := [...]int{1,2,3,4}
for idx, value := range(arr5) { // 返回两个值分别为下标和元素的值
fmt.Println("idx ", idx, " value ", value)
}
for _, value := range(arr5) { // 不使用下标,可以用匿名变量替代
fmt.Println(" value ", value)
}
切片
数组长度是不可变的,于是就有了可变长度的切片。一个切片由数组、长度、容量三个元素组成
切片的声明
切片的声明有多种
从数组
从一个已经存在的数组声明切片
arr := [4]int{1,2,3,4}
sli1 := arr[0:4] // 从数组声明一个切片,[letf, right]表示切片的下标范围,包含left,不包含right
sli2 := arr[2:] // 到数组末尾,可省略右边区间,等价于arr[2:4]
sli3 := arr[:3] // 从数组第一个元素开始,可省略左边区间,等价于arr[0:4]
sli4 := arr[:] // 头尾均省略,等价于arr[0:4]
make函数
使用make函数创建切片。make有三个参数,第一个表示类型,第二个表示长度,第三个表示容量(可省略)
sli5 := make([]int, 10) // 参数中的[]int表示创建的类型,10表示切片长度,此时容量也是10
sli6 := make([]int, 10, 15) // 15 表示数组的容量,容量大于等于长度
切片的操作
- len()返回长度
- cap()返回底层数组容量
- append()追加元素
- copy()复制切片
fmt.Println(len(sli6)) // 长度
fmt.Println(cap(sli6)) // 底层数组容量
sli6 = append(sli6, 6) // 在末尾追加
sli8 := make([]int, 10, 15)
copy(sli8, sli6) // 从sli6拷贝到sli8
fmt.Println(sli8)
map
声明
map的声明有两种,make和直接初始化
var string2int map[string]int = make(map[string]int, 123) // make初始化map,123为容量,可省略
string2int["abc"]=1 // 赋值
int2string := map[int]string{} // 直接声明并初始化为空
int2string2 := map[int]string{1:"a", 2:"b"} // 直接声明并用值初始化
map的操作
// 获取元素个数
fmt.Println(len(int2string2))
// 获取元素值
var value string
var exist bool
value, exist = int2string[1] // 获取元素,返回两个字段,第一个为value值,第二个为是否存在
if exist {
fmt.Println(value)
}
fmt.Println(int2string[3]) // 对于不存在的元素,获取到的值为0值
// range 遍历
for key, value := range int2string2 {
fmt.Println("key:", key, " value:", value)
}
// 删除map中的元素
delete(int2string2, 1)
值传递介绍
对于函数中的变量传递,遵循以下原则:
- 基本类型:值传递
- 数组:值传递
- slice:引用传递
- map:引用传递
下面举例说明
func main() {
iValue := 123
intChange(iValue) // 修改iValue的值
fmt.Println(iValue) // 输出123,值未改变
array := [3]int{1,2,3}
arrayChange(array)
fmt.Println(array) // {1,2,3}值未改变
sli := []int{1,2,3}
sliceChange(sli)
fmt.Println(sli) // {2,2,3}值改变
int2int := map[int]int{} // 空map
mapChange(int2int)
fmt.Println(int2int) // 有值
}
func intChange(value int) {
// value的值是拷贝得来
value = value + 1
}
func arrayChange(arr [3]int) {
arr[0] = arr[0] + 1
arr[1] = arr[1] + 1
arr[2] = arr[2] + 1
}
func sliceChange(sli []int) {
sli[0] = sli[0] + 1
}
func mapChange(m map[int]int) {
m[0] = 123
}
如果想通过函数来改变基本类型和数组的值,需要用指针来操作
func changeValueByPointer() {
iValue2 := 100
intChangeByPonter(&iValue2)
fmt.Println(iValue2) // 101 值改变
array1 := [3]int{1,2,3}
arrayChangeByPointer(&array1)
fmt.Println(array1) // {2,2,3} 值改变
}
func intChangeByPonter(valuep *int) {
*valuep = *valuep + 1
}
func arrayChangeByPointer(arrp *[3]int) {
(*arrp)[0] = (*arrp)[0] + 1
}
控制结构
控制结构包含三部分内容
- if 条件判断
- switch 条件选择
- for 循环
if 条件判断
与c、java等语言不同,go语言的if语句不需要用小括号包起来,且if与条件之间需要有空格、if与{要在同一行、条件与{之间需要有空格。
a := 10
if a > 0 {
fmt.Println(a)
}
更近一步,在if和判断语句之间,可以有一个初始化的语句,与判断语句用逗号分隔:
func main() {
if b := 10; b > 0 { // 初始化局部变量b
fmt.Println(b) // b的作用域在if整个语句内
}
if c := getValue(); c > 0 { // 调用函数初始化
fmt.Println(c)
}
}
func getValue() int {
return 10
}
但在实际使用中,建议将值的初始化和条件分开。
多个条件判断,同样是用if-else来实现
func ifElseDemo() {
if value := getValue(); value == 0 {
fmt.Println("value == 0")
} else if value > 0 { // value的作用域是整个if语句,这里仍然可以访问
fmt.Println("value > 0")
} else {
fmt.Println("value < 0")
}
}
注意:即使条件成立需要执行的语句只有一行,也需要用大括号包裹起来,不能省略。这是其他语言建议的格式,go语言强制这样做
switch 条件选择
go的switch有以下特点:
- switch 后面可以跟任何类型的值匹配,不限于整数
- 同时switch后面可以跟一个简单的初始化语句,类似if。
- 每个case语句结束后,不需要使用break
- 新增fallthrough关键字,可以跳过下一条的语句判断,直接执行语句
func switchDemo() {
rand.Seed(time.Now().Unix())
a := rand.Intn(5)
fmt.Println(a)
switch a {
case 0, 1 :
fmt.Println("a in (0, 1)")
fallthrough // 不会判断 case 2, 3 是否满足,直接执行 fmt.Println("a in (2, 3)")
case 2, 3 :
fmt.Println("a in (2, 3)")
case 4 :
fmt.Println("a = 4")
}
}
for循环
go中的循环只有for关键字,没有while和都-while关键字,但是可以实现相同的功能
while (true)
使用for不加条件来实现while (true)功能
i := 1
for {
fmt.Println(i)
if i > 10 {
break
}
i++
}
while (condition)
使用 for condition 来实现
j := 0
for j > 10 { // 判断条件挪到这里
fmt.Println(j)
j++
}
其他语言的for结构
即 for init; condition; post 来执行循环
for k := 0; k < 10; k++ {
fmt.Println(k)
}
range 遍历
range遍历用来访问map、数组、切片等
int2int := make(map[int]int)
for key, value := range int2int { // map遍历
fmt.Println(key, value)
}
intSli := make([]int, 10, 10)
for index, value := range intSli {
fmt.Println(index, value)
}