第2章 顺序编程
2.1 变量
- 变量是几乎所有编程语言中基本的组成元素
- 程序可以通过定义一个变量来申请一块数据存储空间
2.1.1 变量声明
- Go语言引入了 关键字var,而类型信息放在变量名之后
var v1 int
var v2 string
var v3 [10]int // 数组
var v4 []int // 数组切片
var v5 struct {
f int
}
var v6 *int // 指针
var v7 map[string]int // map,key为string类型,value为int类型
var v8 func(a int) int
var (
v1 int
v2 string
)
2.1.2 变量初始化
- var v1 int = 10 //正确的使用方式1
- var v2 = 10 // 正确的使用方式2,编译器可以自动推倒出v2的类型
v3:=10 // 正确的使用方式3,编译器可以自动推导出v3的类型
2.1.3 变量初始化
- 对于声明变量时需要进行初始化的场景,var关键字可以保留,但不再是必要的元素
var v10 int
v10 =123
- Go语言的变量赋值,支持多重赋值功能
- 比如下面这个交换i和j变量的语句: i,j = j,i
2.1.4 匿名变量
func GetName() (firstName, lastName, nickName string) { return "May", "Chan", "Chibi Maruko" }
若只想获得nickName,则函数调用语句可以用如下方式编写:
_, _, nickName := GetName()
2.2 常量
- 在Go语言中,常量是指编译期间就已知且不可改变的值
- 常量可以是数值类型(包括整型、 浮点型和复数类型)、布尔类型、字符串类型等.
2.2.1 字面常量
- 字面常量(literal),是指程序中硬编码的常量
2.2.2 常量定义
- 通过const关键字,你可以给字面常量指定一个友好的名字
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值 const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", // 无类型整型和字符串常量 Go
- Go的常量定义可以限定常量类型,但不是必需的。
- 如果定义常量时没有指定类型,那么它 与字面常量一样,是无类型常量。
- 由于常量的赋值是一个编译期行为,所以右值不能出现任何需要运行期才能得出结果的表达式
2.2.3 预定义常量
- Go语言预定义了这些常量:true、false和iota。
- iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被 重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。
2.2.4 枚举
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday numberOfDays // 这个常量没有导出 )
- 同Go语言的其他符号(symbol)一样,以大写字母开头的常量在包外可见
- 以上例子中numberOfDays为包内私有,其他符号则可被其他包访问。
2.3 类型
-
Go语言内置以下这些基础类型:
- 布尔类型:bool
- 整形:int8 、byte 、int16 、int 、uint 、uintptr等
- 浮点类型:float32、float64
- 复数类型:complex64、complex128
- 字符串 :string
- 字符类型 : rune
- 错误类型:error
-
此外,Go语言也支持以下这些复合类型:
- 指针(pinter)
- 数组(array)
- 切片(slice)
- 字典(map)
- 通道(chan)
- 结构体(stuct)
- 接口(interface)
-
2.3.1 布尔类型
- 布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换
- 可赋值为预定义的true和 false
//示例代码
var v1 bool
v1 =true
v2 := (1==2)
2.3.2 整形
类型 |
长度(字节) |
值范围 |
int8 |
1 |
-128~127 |
uint8(即byte) |
1 |
0~255 |
int16 |
2 |
-32 768 ~ 32 767 |
uint16 |
2 |
0 ~ 65 535 |
int32 |
4 |
-2 147 483 648 ~ 2 147 483 647 |
uint32 |
4 |
0 ~ 4 294 967 295 |
int64 |
8 |
-9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 |
uint64 |
8 |
0 ~ 18 446 744 073 709 551 615 |
int |
平台相关 |
平台相关 |
uint 平台相关 |
平台相关 |
uintptr |
同指针 |
在32位平台下为4字节,64位平台下为8字节 |
1. 类型表示
- int和int32在Go语言里被认为是两种不同的类型,编译器也不会帮你自动 做类型转换
var value2 int32
value1 := 64 // value1将会被自动推导为int类型 value2 = value1 // 编译错误
value2 = int32(value1) // 编译通过
- 开发者在做强制类型转换时,需要注意数据长度被截短而发生的数据精度损失(比如 将浮点数强制转为整数)和值溢出(值超过转换的目标类型的值范围时)问题。
2. 数值运算
- Go语言支持下面的常规整数运算:+、、*、/和%
3. 比较运算
- Go语言支持以下的几种比较运算符:>、<、==、>=、<=和!=。这一点与大多数其他语言相 同,与C语言完全一致。
- 两个不同类型的整型数不能直接比较,比如int8类型的数和int类型的数不能直接比较,但 各种类型的整型变量都可以直接与字面常量(literal)进行比较。
var i int32
var j int64
i, j = 1, 2
if i == j { // 编译错误
fmt.Println("i and j are equal.")
}
if i == 1 || j == 2 { // 编译通过 fmt.Println("i and j are equal.")
}
4. 位运算
运算 |
含义 |
样例 |
x<<y |
左移 |
124 << 2 // 结果为496 |
x >> y |
右移 |
124 >> 2 // 结果为31 |
x ^ y |
异或 |
124 ^ 2 // 结果为126 |
x & y |
与 |
124 & 2 // 结果为0 |
x | y |
或 |
124 | 2 // 结果为126 |
^x |
取反 |
^2 // 结果为-3 |
2.3.3 浮点型
1. 浮点数表示
- Go语言定义了两个类型float32和float64
- 其中float32等价于C语言的float类型, float64等价于C语言的double类型。
2. 浮点数比较
- 因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等 是不可行的,这可能会导致不稳定的结果。
import "math"
// p为用户自定义的比较精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
2.3.4 复数类型
- 复数实际上由两个实数(在计算机中用浮点数表示)构成
- 一个表示实部(real),一个表示 虚部(imag)
1. 复数表示
var value1 complex64 // 由2个float32构成的复数类型
value1 = 3.2 +12i
value2 :=3.2 +12i
value3 :=complex(3.2,12)
2.实部与虚部
- 对于一个复数z = complex(x, y),就可以通过Go语言内置函数real(z)获得该复数的实 部,也就是x,通过imag(z)获得该复数的虚部,也就是y。
2.3.5 字符串
var str string // 声明一个字符串变量
str ="hello word"
ch:=str[0] //取字符串的第一个字符
fmt.Println("The length of \"%s\" is %d \n ",str,len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
- 字符串的内容可以用类似于数组下标的方式获取,但与数组不同,字符串的内容不能在初始 化后被修改
1. 字符串操作
- x+y 字符串链接
- len(s) 字符串长度
- s[i] 取字符
- 更多字符串操作,请参考标准库strings包
2. 字符串遍历
- Go语言支持两种方式遍历字符串
- 一种是以字节数组的方式遍历
str := "Hello,世界"
n := len(str)
for i := 0; i < n; i++ {
ch := str[i] // 依据下标取字符串中的字符,类型为byte fmt.Println(i, ch)
}
str := "Hello,世界"
for i, ch := range str {
fmt.Println(i, ch)//ch的类型为rune
}
- 以Unicode字符方式遍历时,每个字符的类型是rune(早期的Go语言用int类型表示Unicode 字符),而不是byte。
字符类型
- 在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符的单个字节的值
- 另一个是rune,代表单个Unicode字符
- 关于rune相关的操作,可查阅Go标准库的unicode包。另外unicode/utf8包也提供了UTF8和Unicode之间的转换。
2.3.7 数组
- 数组就是指一系列同一类型数据 的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数 组的长度。
- 以下为一些常规的数组声明方法:
[32]byte //长度为32的数组,每个元素为一个字节
[2*N] struct {
x,y int32
}
[1000] *float64 //指针数组
[3][5] int //二维数组
[2][2][2] float64 //等同于[2]([2]([2]float64))
1. 元素访问
for i := 0; i < len(array); i++ {
fmt.Println("Element", i, "of array is", array[i])
}
- Go语言还提供了一个关键字range,用于便捷地遍历容器中的元素
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}
2. 值类型
- 在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。因此,在函数体中无法修改传入的数组的内容。
pacage main
import "fmt"
func modify (array [10] int) {
array[0] =10
fmt.Println("In modify (),array values : ", array)
}
funct main(){
array := [5] int{1,2,3,4,5} //定义并初始化一个数组
modify(array) //传递一个函数,并试图在函数体内修改这个数组内容
fmt.Println("In main() , array values : " ,array)
}
2.3.8 数组切片
- 数组的特点:数组的长度在定义之后无法再次修改;
- 数组是值类型, 每次传递都将产生一份副本
- 数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构
- 数组切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针;
- 数组切片中的元素个数;
- 数组切片已分配的存储空间。
1. 创建数组切片 创建数组切片的方法
- 创建数组切片的方法主要有两种——基于数组和直接创建,下面我们来简要介绍一下这两种方法
基于数组
- 数组切片可以基于一个已存在的数组创建。
- 数组切片可以只使用数组的一部分元素或者整个 数组来创建,甚至可以创建一个比所基于的数组还要大的数组切片。
pacage main
import "fmt"
func main(){
var myArray [10] int = [10]{1,2,3,4,5,6,7,8,9,10}
var mySlice []int = myArray[:5]
fmt.Println("Elements of myArray: ")
for _,v := range myArray {
fmt.Print(v, " ")
}
fmt.Println()
}
- Go语言支持用myArray[first:last]这样的方式来基于数组生成一 个数组切片,而且这个用法还很灵活
- 基于myArray的所有元素创建数组切片: mySlice = myArray[:]
- 基于myArray的前5个元素创建数组切片: mySlice = myArray[:5]
- 基于从第5个元素开始的所有元素创建数组切片: mySlice = myArray[5:]
直接创建
- Go语言提供的内置函数make()可以用于 灵活地创建数组切片.
- 创建一个初始元素个数为5的数组切片,元素初始值为0:
- mySlice1 :=make ([]int ,5)
- mySlice2 := make([]int, 5, 10)
- mySlice3 := []int{1, 2, 3, 4, 5}
2. 元素遍历
- 操作数组元素的所有方法都适用于数组切片,比如数组切片也可以按下标读写元素,用len() 函数获取元素个数,并支持使用range关键字来快速遍历所有元素
- 传统的元素遍历方法如下:
for i:= 0;i<len(mySlice);i++ {
fmt.Println("mySlice[",i,"]=",mySlice[i])
}
- 使用range关键字可以让遍历代码显得更整洁。range表达式有两个返回值,第一个是索引, 第二个是元素的值:
for i, v := range mySlice {
fmt.Println("mySlice[", i, "] =", v)
}
3. 动态增减元素
- 可动态增减元素是数组切片比数组更为强大的功能
- 数组切片多了一个存储能力(capacity)的概念,即元素个数和分配的空间可以是两个不同的值
- 数组切片支持Go语言内置的cap()函数和len()函数
- cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是 数组切片中当前所存储的元素个数。
package main
import "fmt"
func main() {
mySlice := make([]int, 5, 10)
fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))
}
- 新增元素,可以使用append()函数
- mySlice = append(mySlice, 1, 2, 3) //从尾端给mySlice加上3个元素
- 接将一个数组切片追加到另一个数组切片的末尾
- mySlice2 := []int{8, 9, 10}
- mySlice = append(mySlice, mySlice2...)
- 在第二个参数mySlice2后面加了三个点,即一个省略号,如果没有这个省 略号的话,会有编译错误,因为按append()的语义,从第二个参数起的所有参数都是待附加的 元素。因为mySlice中的元素类型为int,所以直接传递mySlice2是行不通的。加上省略号相 当于把mySlice2包含的所有元素打散后传入。
- 数组切片会自动处理存储空间不足的问题。如果追加的内容长度超过当前已分配的存储空间 (即cap()调用返回的信息),数组切片会自动分配一块足够大的内存
4. 基于数组切片创建数组切片
oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3个元素构建新数组切片
5. 内容复制
- 数组切片支持Go语言的另一个内置函数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个位置
- 如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行 复制。
2.3.9 map
package main
import "fmt"
type PersonInfo struct {
ID string
Name string
Address string
}
func main () {
var personDB map[string] PersonInfo
personDB = make(map[string] PersonInfo)
personDB["123456"] = PersonInfo{"123456","TOM","ROOM203",...}
personDB["1"]= PersonInfo{"1","jack","Room01",...}
person , ok:=personDB["1234"]
if ok {
fmt.Println("Found person", person.Name, "with ID 1234.")
fmt.Println("Did not find person with ID 1234.")
}
}
1. 变量声明
- var myMap map[string] PersonInfo
- myMap是声明的map变量名,string是键的类型,PersonInfo则是其中所存放的值类型
2. 创建
- 使用Go语言内置的函数make()来创建一个新map
- myMap = make(map[string] PersonInfo)
- 指定该map的初始存储能力:
- myMap = make(map[string] PersonInfo, 100)
- 创建并初始化map的代码如:
myMap = map[string] PersonInfo{
"1234": PersonInfo{"1", "Jack", "Room 101,..."},
}
3. 元素赋值
- 赋值过程非常简单明了,就是将键和值用下面的方式对应起来即可:
myMap["1234"] = PersonInfo{"1", "Jack", "Room 101,..."}
4. 元素删除
delete(myMap, "1234")
5. 元素查找
comming song...
网友评论