介绍go类型
类型
字符串
- 字符串的内容可以用类似于数组下标的方式获取,但与数组不同,字符串的内容不能在初始化后被修改,比如以下的例子:
str := “Hello world” // 字符串也支持声明时进行初始化的做法
str[0] = ‘X’ // 编译错误 - 字符串操作
|运算 |含义| 样例|
|:—-|:—–|:—–|
|a + b|连接字符串|”Hello”+”world” //输出为”helloworld”|
|len(s)|求字符串长度|len(“louis”) //输出为5|
|s[i]|取字符|str = “louis”,str[1] //为’o’|
3.
数组
以下为一些常规的数组声明方法:1
2
3
4
5[32]byte // 长度为32的数组,每个元素为一个字节
[2*N] struct { x, y int32 } // 复杂类型数组
[1000] *float64 // 指针数组
[3][5]int // 二维数组
[2][2][2]float64 // 等同于[2]([2]([2]float64))
- 元素访问
可以使用数组下标来访问数组中的元素。与C语言相同,数组下标从0开始, len(array)-1
则表示最后一个元素的下标。下面的示例遍历整型数组并逐个打印元素内容:
for i := 0; i < len(array); i++ {
fmt.Println(“Element”, i, “of array is”, array[i])
}
Go语言还提供了一个关键字 range ,用于便捷地遍历容器中的元素。当然,数组也是 range的支持范围。上面的遍历过程可以简化为如下的写法:
for i, v := range array {
fmt.Println(“Array element[“, i, “]=”, v)
} - 值类型
需要特别注意的是,在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func modify(array [10]int) {
array[0] = 10 // 试图修改数组的第一个元素
fmt.Println("In modify(), array values:", array)
}
func main() {
array := [5]int{1,2,3,4,5} // 定义并初始化一个数组
modify(array)// 传递给一个函数,并试图在函数体内修改这个数组内容
fmt.Println("In main(), array values:", array)
}
该程序的执行结果为:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]
数组切片
创建数组切片
- 基于数组
- 直接创建
饼非一定要事先准备一个数组才能创建数组切片。Go语言提供的内置函数 make() 可以用于灵活地创建数组切片。下面的例子示范了直接创建数组切片的各种方法。
创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}
当然,事实上还会有一个匿名数组被创建出来,只是不需要我们来操心而已。
动态增减元素
可动态增减元素是数组切片比数组更为强大的功能。与数组相比,数组切片多了一个存储能力(capacity)的概念,即元素个数和分配的空间可以是两个不同的值。合理地设置存储能力的值,可以大幅降低数组切片内部重新分配内存和搬送内存块的频率,从而大大提高程序性能。
假如你明确知道当前创建的数组切片最多可能需要存储的元素个数为50,那么如果你设置的存储能力小于50,比如20,那么在元素超过20时,底层将会发生至少一次这样的动作——重新分配一块“够大”的内存,并且需要把内容从原来的内存块复制到新分配的内存块,这会产生比较明显的开销。
数组切片支持Go语言内置的 cap() 函数和
len()函数,代码清单2-2简单示范了这两个内置函数的用法。可以看出, cap()函数返回的是数组切片分配的空间大小,而 len() 函数返回的是数组切片中当前所存储的元素个数。1
2
3
4
5
6
7package main
import "fmt"
func main() {
mySlice := make([]int, 5, 10)
fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))
}
该程序的输出结果为:
len(mySlice): 5
cap(mySlice): 10
如果需要往上例中 mySlice 已包含的5个元素后面继续新增元素,可以使用 append() 函数。下面的代码可以从尾端给 mySlice 加上3个元素,从而生成一个新的数组切片:5
mySlice = append(mySlice, 1, 2, 3)
函数 append() 的第二个参数其实是一个不定参数,我们可以按自己需求添加若干个元素,甚至直接将一个数组切片追加到另一个数组切片的末尾:
mySlice2 := []int{8, 9, 10}// 给mySlice后面添加另一个数组切片
mySlice = append(mySlice, mySlice2…)
需要注意的是,我们在第二个参数 mySlice2后面加了三个点,即一个省略号,如果没有这个省略号的话,会有编译错误,因为按 append() 的语义,从第二个参数起的所有参数都是待附加的元素。因为 mySlice 中的元素类型为 int ,所以直接传递 mySlice2 是行不通的。加上省略号相当于把 mySlice2 包含的所有元素打散后传入
上述调用等同于:
mySlice = append(mySlice, 8, 9, 10)
数组切片会自动处理存储空间不足的问题。如果追加的内容长度超过当前已分配的存储空间(即 cap() 调用返回的信息),数组切片会自动分配一块足够大的内存。
内容复制
数组切片支持Go语言的另一个内置函数 copy() ,用于将内容从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。下面的示例展示了 copy() 函数的行为:
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
map类型
流程控制
条件语句
1 | if a < 5 { |
关于条件语句,需要注意以下几点:
- 条件语句不需要使用括号将条件包含起来 () ;
- 无论语句体内有几条语句,花括号 {} 都是必须存在的;
- 左花括号 { 必须与 if 或者 else 处于同一行;
- 在 if 之后,条件语句之前,可以添加变量初始化语句,使用 ; 间隔;
- 在有返回值的函数中,不允许将“最终的” return 语句包含在 if…else… 结构中,
否则会编译失败:
function ends without a return statement。
失败的原因在于,Go编译器无法找到终止该函数的 return 语句。编译失败的案例如下:
func example(x int) int {
if x == 0 {
} else {return 5
}return x
}
选择语句
根据传入条件的不同,选择语句会执行不同的语句。下面的例子根据传入的整型变量 i 的不
同而打印不同的内容:
switch i {
case 0:
fmt.Printf(“0”)
case 1:
fmt.Printf(“1”)
case 2:
fallthrough
case 3:
fmt.Printf(“3”)
case 4, 5, 6:
fmt.Printf(“4, 5, 6”)
default:
fmt.Printf(“Default”)
}
运行上面的案例,将会得到如下结果:
i = 0 时,输出 0 ;
i = 1 时,输出 1 ;
i = 2 时,输出 3 ;
比较有意思的是, switch 后面的表达式甚至不是必需的,比如下面的例子:
switch {
case 0 <= Num && Num <= 3:
fmt.Printf(“0-3”)
case 4 <= Num && Num <= 6:
fmt.Printf(“4-6”)
case 7 <= Num && Num <= 9:
fmt.Printf(“7-9”)
}
在使用 switch 结构时,我们需要注意以下几点:
- 左花括号 { 必须与 switch 处于同一行;
- 条件表达式不限制为常量或者整数;
- 单个 case 中,可以出现多个结果选项;
- 与C语言等规则相反,Go语言不需要用 break 来明确退出一个 case ;
- 只有在 case 中明确添加 fallthrough 关键字,才会继续执行紧跟的下一个 case ;
- 可 以 不 设 定 switch 之 后 的 条 件 表 达 式 , 在 此 种 情 况 下 , 整 个 switch 结 构 与 多 个if…else… 的逻辑作用等同。
循环语句
使用循环语句时,需要注意的有以下几点。
- 左花括号 { 必须与 for 处于同一行。
- Go语言中的 for 循环与C语言一样,都允许在循环条件中定义和初始化变量,唯一的区 别是,Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初 始化多个变量。
- Go语言的 for 循环同样支持 continue 和 break
来控制循环,但是它提供了一个更高级的break ,可以选择中断哪一个循环,如下例:1
2
3
4
5
6
7
8
9
10
11for j := 0; j < 5; j++ {
for i := 0; i < 10; i++ {
if i > 5 {
break JLoop
}
fmt.Println(i)
}
}
JLoop:
// ...
本例中, break 语句终止的是 JLoop 标签处的外层循环