1 Go语言运算符
1.1 算数运算符
a := 30 b := 20
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | a + b = 50 |
- | 减号 | a - b = 10 |
* | 乘 | a * b = 600 |
/ | 除(求商) | a / b = 1 |
% | 除(求余) | a % b = 10 |
注意 ++
和--
在Go语言中为单独的语句,不是运算符。
1.2 关系运算符
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
1.3 逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。 |
II | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。 |
! | 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。 |
1.4 位运算符
位运算符对整数在内存中的二进制位进行操作。
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。(两位均为1才为1) |
I | 参与运算的两数各对应的二进位相或。(两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 |
<< | 左移n位就是乘以2的n次方。“a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。“a>>b”是把a的各二进位全部右移b位。 |
1.5 赋值运算符
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
<<= | 左移后赋值 |
>>= | 右移后赋值 |
&= | 按位与后赋值 |
I= | 按位或后赋值 |
^= | 按位异或后赋值 |
2 Go语言流程控制
Go语言提供了条件分支 if
,状态分支switch
,循环 for
,跳转 goto
,延迟执行 defer
,这些流程控制语句。
2.1 if else 条件分支结构
依据条件是否满足确定执行哪个分支。
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
当表达式1的结果为true
时,执行分支1,否则判断表达式2,如果满足则执行分支2,都不满足时,则执行分支3。if
判断中的else if
和else
都是可选的,可以根据实际需要进行选择。
Go语言规定与if
匹配的左括号{必须与if和表达式放在同一行}放在其他位置会触发编译错误。 同理,与else
匹配的{也必须与else写在同一行},else
也必须与上一个if
或else if
右边的大括号在同一行。
score := 85
if score >= 90 {
fmt.Println("优秀")
} else if score > 80 {
fmt.Println("良好")
} else if score > 60{
fmt.Println("及格")
}else {
fmt.Println("不及格")
}
思考:上面这种if
条件判断和下面这种条件判断区别是什么?
if score := 85; score >= 90 {
fmt.Println("优秀")
} else if score > 80 {
fmt.Println("良好")
} else if score > 60{
fmt.Println("及格")
}else {
fmt.Println("不及格")
}
//作用域
2.2 for 循环结构
Go语言中没有while
循环,所有循环类型均可以使用for
关键字来完成。
for 初始语句;条件表达式;结束语句{
循环体语句
}
条件表达式返回true
时循环体不停地进行循环,直到条件表达式返回false
时自动退出循环。
func main() {
for i := 0; i < 10; i++ {
fmt.Print(i) //打印 0-9
}
}
for
循环的初始语句可以被忽略,但是初始语句后的分号必须要写,例如:
func main() {
i := 0
for ; i < 10; i++ {
fmt.Print(i)
}
}
for循环的初始语句和结束语句都可以省略,例如:
func main() {
i := 0
for i < 10 {
fmt.Print(i)
i++
}
}
这种写法类似于其他编程语言中的while,在while后添加一个条件表达式,满足条件表达式时持续循环,否则结束循环。
无限循环
for {
循环体语句
}
for
循环可以通过break
、goto
、return
、panic
语句强制退出循环。
2.3 for range 键值循环
Go语言中可以使用for range
遍历数组、切片、字符串、map
及通道(channel
)。 通过for range
遍历的返回值有以下规律:
- 数组、切片、字符串返回索引和值。
map
返回键和值。- 通道(
channel
)只返回通道内的值。
switch case
使用switch
语句可方便地对大量的值进行条件判断。
func main() {
finger := 3
switch finger {
case 1:
fmt.Print("大拇指")
case 2:
fmt.Printl("食指")
case 3:
fmt.Print("中指")
case 4:
fmt.Print("无名指")
case 5:
fmt.Print("小拇指")
default:
fmt.Print("无效的输入!")
}
}
Go语言规定每个switch
只能有一个default
分支。
一个分支可以有多个值,多个case
值中间使用英文逗号分隔。
func main() {
switch n := 7; n {
case 1, 3, 5, 7, 9:
fmt.Println("奇数")
case 2, 4, 6, 8:
fmt.Println("偶数")
default:
fmt.Println(n)
}
}
分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量,例如:
func main() {
age := 30
switch {
case age < 25:
fmt.Println("年轻")
case age > 25 && age < 35:
fmt.Println("快老了")
case age > 60:
fmt.Println("广场舞")
default:
fmt.Println("25岁||35岁||60岁")
}
}
fallthrough
语法可以执行满足条件的case的下一个case
,是为了兼容C语言中的case
设计的。
func main() {
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough
case s == "b":
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}
}
输出
a
b
goto
跳转
goto
语句通过标签进行代码间的无条件跳转。goto
语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto
语句能简化一些代码的实现过程。 例如双层嵌套的for
循环要退出时:
func main() {
var breakFlag bool
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
breakFlag = true
break
}
fmt.Printf("%v-%v\n", i, j)
}
// 外层for循环判断
if breakFlag {
break
}
}
}
使用goto
语句能简化代码:
func gotoDemo2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v\n", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")
}
break
跳出循环
break
语句可以结束for
、switch
和select
的代码块。
break
语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的for
、switch
和select
的代码块上。 举个🌰:
func main() {
breakDEMO:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
break breakDEMO
}
fmt.Printf("%v-%v\n", i, j)
}
}
fmt.Println("...")
}
contine
继续下一个循环
continue
语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for
循环内使用。
在continue
语句后添加标签时,表示开始标签对应的循环。举个🌰:
func main() {
forloop:
for i := 0; i < 5; i++ {
// forloop2:
for j := 0; j < 5; j++ {
if i == 2 && j == 2 {
continue forloop
}
fmt.Printf("%v-%v\n", i, j)
}
}
}
3 Go语言数组
Go 语言提供了数组类型的数据结构。
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。
相对于去声明 number0, number1, …, number99 的变量,使用数组形式 numbers[0], numbers[1] …, numbers[99] 更加方便且易于扩展。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
3.1 Array 数组
数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。 基本语法:
// 定义一个长度为3元素类型为int的数组a
var a [3]int
3.1.1 数组定义
var 数组变量名 [元素数量]T
比如:var a [5]int
, 数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。[5]int
和[10]int
是不同的类型。
var a [3]int
var b [4]int
a = b //不可以这样做,因为此时a和b是不同的类型
数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,访问越界(下标在合法范围之外),则触发访问越界(panic)
3.1.2 数组初始化
方法一:初始化数组时可以使用初始化列表来设置数组元素的值。
func main() {
var testArray [3]int //数组会初始化为int类型的零值
var numArray = [3]int{1, 2} //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2 0]
fmt.Println(cityArray) //[北京 上海 深圳]
}
方法二:按照上面的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度。
func main() {
var testArray [3]int
var numArray = [...]int{1, 2}
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2]
fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int
fmt.Println(cityArray) //[北京 上海 深圳]
fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
}
方法二:使用指定索引值的方式来初始化数组。
func main() {
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]
fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}
3.1.3 数组的遍历
两种方法遍历数组,普通for
和for range
func main() {
var a = [...]string{"北京", "上海", "深圳"}
// 方法1:for循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// 方法2:for range遍历
for index, value := range a {
fmt.Println(index, value)
}
}
3.1.4 数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
func modifyArray(x [3]int) {
x[0] = 100
}
func modifyArray2(x [3][2]int) {
x[2][0] = 100
}
func main() {
a := [3]int{10, 20, 30}
modifyArray(a) //在modify中修改的是a的副本x
fmt.Println(a) //[10 20 30]
b := [3][2]int{
{1, 1},
{1, 1},
{1, 1},
}
modifyArray2(b) //在modify中修改的是b的副本x
fmt.Println(b) //[[1 1] [1 1] [1 1]]
}
注意:
- 数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。
- [n]* T表示指针数组,*[n]T表示数组指针 。