第四章:流程控制
一、流程控制作用
用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的模块
二、if判断
1、if单分支
条件表达式:
if 条件表达式 { 逻辑代码 }
例:
package main import "fmt" func main() { if count := 30 ; count < 40 { fmt .Println(count) } }
2、if双分支
条件表达式: if 条件表达式 { 逻辑代码 } else { 逻辑代码 }
package main import "fmt" func main() { if count := 30 ; count > 40 { fmt.Println(count) } else { fmt.Println("30不小于40") } }
3、if多分支
条件表达式
if 条件表达式1 { 逻辑代码1 } else if 条件表达式2 { 逻辑代码2 } ... else { 逻辑代码n }
package main import "fmt" func main() { //实现功能:更具数值,判断等级 // >=90 A // >=80 B // >=70 C // >=60 D // <60 C if num :=59; num >= 90 { fmt.Println("A") } else if num >= 80 { fmt.Println("B") } else if num >= 70 { fmt.Println("C") } else if num >= 60 { fmt.Println("D") } else { fmt.Println("E") } }
package main import ( "fmt" "math" ) func main() { var a,b,c float64 = 3,100,5 d := b * b - 4 * a * c if d > 0 { x1 := (-b + math.Sqrt(d)) / 2 * a x2 := (-b - math.Sqrt(d)) / 2 * a fmt.Printf("x1的值为:%v, x2的值为:%v", x1,x2) } else if d == 0 { x1 := (-b + math.Sqrt(d)) / 2 * a fmt.Printf("x1的值为:%v", x1) } else { fmt.Println("误解") } var high int32 var money float64 var hand bool fmt.Println("请输入身高") fmt.Scanln(&high) fmt.Println("请输入资产") fmt.Scanln(&money) fmt.Println("帅与否") fmt.Scanln(&hand) if high > 180 && money > 1.0 && hand { fmt.Println("非得嫁") } else if high > 180 || money > 1.0 || hand { fmt.Println("嫁") } else { fmt.Println("不嫁") } }
二、switch分支
表达式
switch 表达式 { case 值1,值2,...: 逻辑代码1 case 值3,值4,...: 逻辑代码2 ... default: 逻辑代码3 }
注意事项: switch后是一个表达式(常量值、变量、一个有返回值的函数) case后如果是一个常量,则不能重复 case后的各个值得数据类型,必须和switch的表达式的数据类型一致 case后边可以有多个值,但需要用 “,”分隔开 case后跟条件表达式,switch后面不需要在跟条件表达式 case后不需要带break default不是必须的,位置也是随意的 switch后也可以不带表达式,当做if分支来使用 switch后也可以直接声明/定义一个变量,分号结束(不推荐) switch穿透,利用fallthrough关键字,如果在case语句块后增加fallthrough ,则会继续执行下一个case,也叫switch穿透.
package main import "fmt" func main() { var num int = 84 switch num/10 { case 10 : fmt.Println("A") fallthrough case 9 : fmt.Println("B") case 8 : fmt.Println("C") default : fmt.Println("D") } }
fallthrough
fallthrough会强制执行后面的case语句,fallthrough不会判断下一条case的表达式结果是否为true
package main import "fmt" func main (){ //判断变量是否大于60 且不大于100 var num int32 fmt.Println("请输入学生成绩:") fmt.Scanln(&num) //case 后跟条件表达式,switch则不用再判断 switch { case num > 100 : fmt.Println("输入有误") case num >= 60 : fmt.Println("成绩合格") default: fmt.Println("成绩不合格") } }
三、select语句
select语句类似于switch语句,但是select会随机执行一个可运行的case。如果没有case可运行,他将阻塞,知道有case可运行
package main import "fmt" func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = <- c1 : fmt.Println("recived", i1, "from c1\n") case c2 <- i2 : fmt.Println("sent ", i2, "tp c2\n") case i3, ok := (<-c3) : if ok { fmt.Println("received ", i3, "from c3\n") } else { fmt.Println("c3 is closed\n") } default : fmt.Println("no communocatopm\n") } }
四、循环结构
1、for循环
语法结构
for init; condition; post {} for condition {} for {} init:一般为赋值表达式,给控制变量赋初值 condition:关系表达式或逻辑表达式,循环控制条件 post:一般为赋值表达式,给控制变量增量或减量 for 初始表达式;布尔表达式;迭代因子 { 循环体; }
package main import "fmt" func main() { var s um int = 0 for i := 1 ; i <= 5 ; i++ { sum += i } fmt.Println(sum) }
2、嵌套for循环
案例:现有3个班,每个班5名学生,求出各个班平均分,所有班级平均分和及格人数
package main import "fmt" func main () { //1.定义变量,班级 class 人数 per 总成绩 total var class int = 3 var per int = 5 var total float64 = 0 var pass int = 0 for j := 1; j <= class; j++ { //定义各个班级学生总成绩 var sum float64 = 0.0 for i := 1; i <= per; i++ { //定义学生成绩 scor var scor float64 fmt.Printf("请输入班级%d,第%d个学生成绩:\n", j,i) fmt.Scanln(&scor) //判断成绩是否及格 if scor >= 60 { pass++ } sum += scor } fmt.Printf("第%d个班级的平均分为:%v\n", j,sum / float64(per)) total += sum } fmt.Println("所有班级的平均分为:", total/float64(per*class)) fmt.Println("及格人数为:", pass) }
3、示例:打印空心金字塔
*
***
*****
*******
*********
第一步:打印矩形
package main import "fmt" func main() { //定义金字塔层数的变量 tier,定义金字塔每层数量的变量 num var tier int = 5 var num int = 5 for i := 1; i <= tier; i++ { for j := 1; j <= num; j++ { fmt.Print("*") } fmt.Println() } }
第二步:打印半个金字塔
package main import "fmt" func main() { //定义金字塔层数的变量 tier,定义金字塔每层数量的变量 num var tier int = 5 //var num int = 5 for i := 1; i <= tier; i++ { for j := 1; j <= i; j++ { fmt.Print("*") } fmt.Println() } }
第三步:打印整个金字塔
package main import "fmt" func main() { //定义金字塔层数的变量 tier,定义金字塔每层数量的变量 num var tier int = 5 //var num int = 5 //i 表示层数 for i := 1; i <= tier; i++ { //打印*前先打印空格 for l := 1; l <= tier - i; l++ { fmt.Print(" ") } //j 表示每层的数量 for j := 1; j <= 2 * i - 1; j++ { fmt.Print("*") } fmt.Println() } }
第四步:打印空心金字塔
package main import "fmt" func main() { //定义金字塔层数的变量 tier,定义金字塔每层数量的变量 num var tier int = 5 //var num int = 5 //i 表示层数 for i := 1; i <= tier; i++ { //打印*前先打印空格 for l := 1; l <= tier - i; l++ { fmt.Print(" ") } //j 表示每层的数量 for j := 1; j <= 2 * i - 1; j++ { //判断合适输出* if j == 1 || j == 2 * i - 1 || i == tier { fmt.Print("*") }else { fmt.Print(" ") } } fmt.Println() } }
五、循环控制
1、break:用于跳出当前for循环或者是switch语句
package main import "fmt" func main() { var num int = 10 for num < 20 { fmt.Printf("num的值为:%d\n", num) num++ if num > 15 { break } } }
输入用户名密码,并判断,如果正确,登录成功,如果错误,提示还有几次机会
package main import "fmt" func main() { var name string var pass int for i := 1; i <= 3; i++ { fmt.Println("请输入用户名:") fmt.Scanln(&name) fmt.Println("请输入密码:") fmt.Scanln(&pass) if name == "张无忌" && pass == 888 { fmt.Println("登陆成功") break }else { fmt.Println("还剩",3 - i,"次机会") } } }
2、continue:跳过当前循环的剩余语句,然后继续进行下一轮循环
package main import "fmt" func main (){ var num int = 10 for num < 20 { if num == 15 { num += 1 continue } fmt.Printf("a的值为:%d\n", num) num++ } }
打印1-100之内的奇数
package main import "fmt" func main () { for i := 1; i <= 100; i++ { if i % 2 != 0 { fmt.Println(i) continue } } }
package main import "fmt" //从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入0时结束程序 //定义变量计数器 var count1 int var count2 int for true { var num int fmt.Printf("请输入数字:\n") fmt.Scanln(&num) if num > 0 { count1++ continue } else if num < 0 { count2++ continue } else if num == 0 { fmt.Println("拜拜") break } fmt.Printf("正数的个数:%d,负数的个数:%d\n", count1,count2) } }
3、goto:将控制转移到被标志的语句
package main import "fmt" func main (){ var num int = 10 //LOOP:给 for 添加标签 LOOP: for num < 20 { if num == 15 { num += 1 //跳到指定的标签 goto LOOP } fmt.Printf("a的值为:%d\n", num) num++ } }
第五章:函数与通道
一、函数介绍
1、定义
完成某一功能的程序指令(语句)的集合,称为函数
函数可以提高代码复用性
2、基本语法
func 函数名 (形参列表) (返回值类型列表) { 执行语句... return + 返回值列表 }
形参是定义在函数体内的局部变量,调用时传递给函数的参数是实参。函数有两种方式传递参数
传递类型 描述
值传递 是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
地址传递 是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将会影响到实际参数
package main import "fmt" func swap (a,b *int){ var c int c = *a *a = *b *b = c } func main() { var x int = 100 var y int = 200 swap(&x,&y) fmt.Println(x,y) }
函数名
首字母大写的函数可以被本包和其他包文件使用
首字母小写的函数只能被本包使用,其他包文件不能使用
3、简单案例
package main import "fmt" //定义函数 func cal (num1 int, num2 int) (int) { sum := 0 sum += num1 sum += num2 return sum } //调用函数 func main() { sum := cal(10,20) fmt.Println(sum) name3 := 11 name4 := 43 sum1 := cal(name3,name4) fmt.Println(sum1) }
package main import "fmt" func main (){ var x int = 100 var y int = 200 sum := add(x,y) sum,avg := calc(x,y) fmt.Println(sum,avg) } func add (a,b int)(sum int){ sum = a + b return sum } func calc (a,b int)(sum int, avg int){ sum = a + b avg = (a + b)/2 return }
4、函数的递归调用
一个函数在函数体内又调用了本身,称为递归调用
示例:
package main import "fmt" func test(n int){ if n > 2 { n-- test(n) } fmt.Println("n=", n) } func main (){ test(4) }
递归函数注意事项
执行一个函数时,就创建一个新的受保护的独立空间(新的函数栈)
函数的局部变量是独立的,不会相互影响
递归必须向递归退出的条件逼近,否则就是无限递归了
当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁
当函数执行完毕或者返回时,该函数本身也会被销毁
练习:
斐波那契数:1,1,2,3,5,8,13,21…
求出第n个数的斐波那契数
package main import "fmt" func fbn(n int) int { if n == 1 || n ==2 { return 1 } else { return fbn(n - 1) + fbn(n - 2) } } func main (){ res := fbn(8) fmt.Println("res=", res) }
5、函数的注意事项
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
值类型(变量直接存储值,内存通常在栈中分配):基本数据类型int系列,float系列,bool、string、数组和结构体struct
引用类型(变量存储的是一个地址,这个地址对应的空间才是真正存放数据的,内存通常在堆山分配,当没有任何变量引用这个地址是,改地址对应的数据空间就会变成一个垃圾,由GC回收):指针、slice切片、map、管道chan、interface等
以值传递方式的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址“&”,函数内以指针的方式操作变量。从效果来看类似引用传递
Golang中支持可变参数(函数支持可变数量的参数)
package main import "fmt" //0个到多个参数,args是slice(切片),用过args[index]可以访问到各个值 //args 是名称,可以随意取名 func myFun(args... int)(sum int) { 函数体 } //1个到多个参数 func myFun1(n1 int, args... int)(sum int) { 函数体 } func main () { }
实例:编写一个函数sum,可以求出 1 到多个int的和
package main import "fmt" //定义一个可变参数的函数 func sum(n1 int, args... int) (sum int) { sum = n1 //遍历args for i := 0; i < len(args); i++ { sum += args[i] } return sum } func main () { rets := sum(4,4,12,42,-4) fmt.Println("rets=", rets) }
Golang支持自定义数据类型(相当于取别名),但在编译中,go还是会认为是两种数据类型 语法: type myInt int
函数不支持重载
在golang中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用
函数既然是一种数据类型,因此函数可以作为形参,并且被调用
package main import "fmt" func getSum(n1 int, n2 int) int { return n1 + n2 } func myFun(funvar func(int, int) int, num1 int, num2 int) int { return funvar(num1, num2) } func main() { res := myFun(getSum, 50, 60) fmt.Println("res=", res) } 支持对函数返回值进行命名 func test (num1 int, num2 int) (sum int, sub int) { sub := num1 - num2 sum := num1 + num2 return }
return 将结果返回给调用者,谁调用就返回给谁
6、init函数
1)、介绍:
没一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用,也就是说init会在main函数前被调用
2)、注意事项:
如果一个文件同事包含全局变量定义,init函数,main函数,则执行的流程是去全局变量 –> init函数 –> main函数
init函数最主要的作用,就是完成一些初始化工作
7、匿名函数
1)、介绍
匿名函数:匿名函数就是我们没有函数名称的函数,匿名函数只包括 参数列表、返回值列表
2)、使用示例
package main import "fmt" func main() { //定义匿名函数时直接调用(只能调用一次) a := func (n1,n2 int) (sum int) { sum = n1 + n2 return }(10,20) //调用匿名函数 fmt.Println("a=", a) fmt.Println("============================") //将匿名函数赋值给变量b,则 b的数据类型就是函数类型,此时可以通过b变量完成调用 b := func (n1,n2 int) int { return n1 - n2 } //调用匿名函数 res := b(10,5) fmt.Println("b=", res) }
3)、全局匿名函数
package main import "fmt" //定义全局匿名函数 var ( Fun = func (n1,n2 int) int { return n1 * n2 } ) func main() { //调用 res := Fun(12,12) fmt.Println("res=", res) }
8、函数的 defer 关键字
在函数中,经常需要创建资源(比如:数据库连接,文件句柄,锁等),为了在函数执行完毕后,及时的释放资源,提供了defer(延时机制)
package main import "fmt" func sum(n1,n2 int) int { //当函数执行遇到 defer 关键字时,会先不执行,编译器会将 defer 后的语句暂时压入到独立的栈中(defer栈) //当函数执行完毕后,再从 defer栈中按照先入后出的方式出栈,执行语句 //在 defer 将语句放入栈时,同时也会将相关的值拷贝入栈 defer fmt.Println("n1=", n1) defer fmt.Println("n2=", n2) n1++ n2++ sum := n1 + n2 fmt.Println("sum=", sum) return sum } func main() { sum := sum(10,20) fmt.Println("main.sum=", sum) }
二、包
1、包的注意事项
package字段对包进行声明,建议,包的声明和这个包坐在的文件夹同名
main包是程序的入口,main函数一定要放在main包下,否则不能编译执行
引入多个包的语法:
import (
“包路径/包名”
“包路径/包名”
)
包名首字母大写,才可以被其他包访问
一个目录下不能有重名的函数
包的名字和文件夹的名字可以不一样
一个目录下的同级文件归属一个包
包在程序层面,所有使用相同package 包名 的源文件组成的代码模块,在源文件层面,包就是一个文件夹
如果要将项目编译成可执行文件,需要将这个包声明为 main 包,即 package main ,如果写的是一个库,报名可以自定义 编译方法:
go build -o 可执行文件存放路径/文件名 包名
2、闭包
闭包就是一个函数与其他相关的引用环境组合的一个整体
package main import "fmt" //AddUpper 是函数名称,返回的数据类型是匿名函数 func (int) int func AddUpper() func(int) int { var n int = 10 //返回的是一个匿名函数,但是这个匿名函数引用到了函数外的变量“n” //因此,这个匿名函数就和变量 n 形成了一个整体,构成闭包 return func(x int) int { n = n + x return n } } func main() { f := AddUpper() fmt.Println(f(1)) fmt.Println(f(2)) }
package main import "fmt" func AddUpper() (func(int) int) { var n int = 10 var str string = "hello" return func(x int) int { str += string(36) fmt.Println("str=", str) n = n + x return n } } func main() { f := AddUpper() fmt.Println(f(1)) fmt.Println(f(2)) fmt.Println(f(3)) fmt.Println(f(4)) fmt.Println(f(5)) }
//编写一个函数 makeSuffix(suffix string) 可以接受一个文件后缀名,并返回一个闭包 //调用闭包可以返回一个文件名,如果改文件名没有指定后缀,则返回文件名+后缀,如果已指定后缀,则不做处理 //strings.HasSuffix ,该函数可以判断某个字符串是否有指定的后缀
package main import ( "fmt" "strings" )
//编写一个函数 makeSuffix(suffix string) 可以接受一个文件后缀名,并返回一个闭包
//调用闭包可以返回一个文件名,如果改文件名没有指定后缀,则返回文件名+后缀,如果已指定后缀,则不做处理
//strings.HasSuffix ,该函数可以判断某个字符串是否有指定的后缀
func makeSuffix(suffix string) (func (name string) string) { return func (name string) string { // 判断 name 后是否有指定后缀,如果没有,则添加后缀 if ! strings.HasSuffix(name,suffix) { return name + suffix } return name } } func main() { file := makeSuffix(".jpg") fmt.Println("处理后的文件名称:", file("wirt")) fmt.Println("处理后的文件名称:", file("test.jpg")) }
三、通道
1、简介 通道(channel)是传递数据的数据结构 通道可用于两个goroutine通过传递一个指定类型的值来同步运行和通讯。操作符<-用户指定通道的方向,发送或接受,如果未指定,则为双向通道 goroutine:是Golang的一种轻量级线程 2、声明通道 使用chan关键字声明通道
ch := make(chan int) //定义一个传输整型数据的通道,默认不带缓冲区 ch <- v //把v发送到通道ch v := <-ch //从通道ch接收数据,并把值赋给v
3、简单示例
package main import "fmt" func main (){ //定义一个存储整型数据类型的带缓冲通道,缓冲区大小2 ch := make(chan int, 2) //因为ch通道带有2个缓冲区,因此可以同时发送两个数据,而不用立刻需要去读取数据 //将数据传入通道 ch <- 1 ch <- 2 //获取两个数据 fmt.Println(<-ch) fmt.Println(<-ch) }
package main import "fmt" func main (){ //定义一个存储整型数据类型的带缓冲通道,缓冲区大小3 var ch chan int ch = make(chan int, 3) //看看intchan是什么 fmt.Printf("1. ch的值为:%v, ch 本身的地址为:%p\n ", ch,&ch) //向通道写入数据 ch <- 10 num := 2211 ch <- num ch <- 23 //fmt.Println("当前chane中的数据是:", <-ch) //从通道取出数据后,可以继续写入 <- ch //fmt.Println("数据取出后,ch的值为:", <-ch) ch <- 98 //看看通道的长度和cap(容量) fmt.Printf("channel len= %v,cap=%v \n", len(ch),cap(ch)) //从通道中读取数据 var num2 int num2 = <- ch fmt.Println(num2, "是变量num2的值") fmt.Printf("channel len= %v,cap=%v \n", len(ch),cap(ch)) //在没有使用协程的情况下,如果我们的管道数据已经被全部取出,再取就会报错 “deadlock” num3 := <- ch num4 := <- ch //num5 := <- ch fmt.Println("num3=", num3,"num4=", num4,/*"num5=", num5*/) }
第六章:错误处理
通过内置的error接口实现错误处理
错误捕获代码演示
package main import "fmt" func test (){ //利用 defer + recover 来捕获错误:defer 后加匿名函数的调用 defer func () { //调用 recover 内置函数,可以捕获错误 err := recover() //如果捕获到错误,返回值为零值:nil if err != nil { fmt.Println("error 已被捕获") fmt.Println("err is :", err) } //'()' 是对匿名函数的调用 }() num1 := 10 num2 := 0 result := num1 / num2 fmt.Println("result value is :", result) } func main (){ test() fmt.Println("/ exec successd: ") fmt.Println("next") }
自定义错误代码演示
package main import ( "fmt" "errors" ) func test ()(err error) { num1 := 10 num2 := 0 if num2 == 0 { //抛出自定义错误 return errors.New("除数不能为‘0’") }else { result := num1 / num2 fmt.Println("result value is :", result) return nil } } func main (){ err := test() if err != nil { fmt.Println("自定义错误:", err) //pacic 内置函数可以让程序终止运行 //pacic 可以接受一个interface{}类型的值(任何值)作为参数 panic(err) } fmt.Println("/ exec successd: ") fmt.Println("next") }
第七章:数组
一、数组的简介
1、数组是特殊的变量,也用 var 字段进行声明
2、数组的下标从 0 开始
3、数组的格式
var 数组名称 [数组长度] 数组类型
4、数组的初始化状态
package main import "fmt" func main() { //定义数组,数组也是特殊的变量 var scores [5] int //将数据存入数组 scores[0] = 81 scores[1] = 41 scores[2] = 78 scores[3] = 99 scores[4] = 21 //求和 sum := 0 for i := 0; i < len(scores); i++ { sum += scores[i] } //平均数 avg := sum / len(scores) fmt.Printf("sum value is %v, avg value is %v", sum,avg) }
5、数组是值类型
6、 数组是过个相同类型数据的组合,一个数组一旦定义了,其长度是固定的,不能动态变化,否则报越界错误
7、数组中的元素可以是任何数据类型,包括之值类型和引用类型,但不能混用
8、数组在传递时,长度是数组类型的一部分
二、数组的应用
1、数组的遍历
package main import "fmt" func main() { //定义数组,数组也是特殊的变量 var scores [5] int //将数据存入数组 for i := 0 ; i < len(scores); i++ { fmt.Printf("请输入数组中第%d个数:", i +1) //监听终端输入的内容,它在遇到换行时才停止监听。最后一个数据后面必须有换行或者到达结束位 置。 fmt.Scanln(&scores[i]) } //利用 for 循环展示数组中的内容 for i := 0; i < len(scores); i++ { fmt.Printf("数组中第%d个数为:%d\n", i + 1,scores[i]) } fmt.Println("=================================================") //利用 for-range 循环展示数组中的内容 for key,value := range scores { fmt.Printf("数组中第%d个内容为:%d\n", key+1,value) } }
2、数组初始化的几种方式
package main import "fmt" func main() { var arr1 [3]int = [3]int{3,6,8} var arr2 = [3]int{2,5,6} var arr3 = [...]int{9,8,7,6,5} var arr4 = [...]int{2:11,3:44,9:22} fmt.Println(arr1,arr2,arr3,arr4) }
3、数组的应用示例
求出一个数组中的最大值与对应的下标
package main import "fmt" func main() { fmt.Println("===================================================") //假设数组中最大的值为下标为0的元素 //将第一个元素循环与之后的元素进行比较,如果小于,就替换 var maxValue int = 0 var maxValueIndex int = 0 var intArr = [...]int{1,3,48,13,84,25,153} for i := 1; i < len(intArr); i++ { if maxValue < intArr[i] { maxValue = intArr[i] maxValueIndex = i } } fmt.Printf("maxValue=%v, maxValueIndex=%v", maxValue,maxValueIndex) }
求数组中元素的和与平均值
package main import "fmt" func main() { var numArr = [...]int{1,32,123,45,42} sum := 0 for _,value := range numArr { sum += value } fmt.Printf("数组中和为:%v, 平均数为:%v\n", sum, float64(sum)/float64(len(numArr))) }
随机生成五个数,并反转打印
package main import ( "fmt" "time" "math/rand" ) func main() { var intArr [5]int rand.Seed(time.Now().UnixNano()) for i := 0; i < len(intArr); i++ { intArr[i] = rand.Intn(100) } fmt.Println("交换前intArr=", intArr) //翻转打印,交换的次数是数组元素个数 / 2 //倒数第一个元素与第一个元素交换,倒数第二个与第二个元素交换 //定义一个临时变量 temp := 0 for i := 0; i < len(intArr) / 2; i++ { temp = intArr[len(intArr) - 1 - i] intArr[len(intArr) - 1 - i] = intArr[i] intArr[i] = temp } fmt.Println("交换后intArr=", intArr) }
三、二维数组
1、二维数组的定义
var 数组名称 [一维数组的个数][一维数组中元素的个数] 数组的类型
package main import "fmt" func main() { var arr5 [4][3]int16 fmt.Println(arr5) }
2、二位数组的初始化
package main import "fmt" func main() { var arr1 [3][3]int = [3][3]int{{1,2,3},{4,5,6},{7,8,9}} fmt.Println(arr1) }
3、二维数组的遍历
package main import "fmt" func main() { var arr [3][3]int = [3][3]int{{1,2,3},{4,5,6},{7,8,9}} fmt.Println(arr) fmt.Println("============================================") // for 循环遍历 for i := 0; i < len(arr); i++ { for j := 0; j < len(arr[i]); j++ { fmt.Printf("arr[%v][%v]=%v\n", i,j,arr[i][j]) } } fmt.Println("==============================================") // for range 循环遍历 for key,value := range arr { for k,v := range value { fmt.Printf("arr[%v][%v]=%v\n",key,k,v) } } }
第八章:切片
一、切片的简介
切片(slice)是golang中一种特有的数据类型
数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在 G o语言的代码里并不是特别常见。相对的切片却是随处 可见的,切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。
切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
切片定义后不可以直接引用,因为此时切片是空的,需要让其引用到一个数组,或者make一个空间共切片使用
切片使用不能越界
可以对切片继续切片
切片是一个引用类型,切片从底层讲是一个数据结构(struct结构体)
二、切片的定义
1、方式一:
var 切片名称 []切片类型 = 数组的一个片段引用
package main import "fmt" func main() { //定义一个数组 var arr [5]int = [5]int{1,2,3,4,5} //定义一个切片 //第一种方法,sli1 切片应用数组 arr 中下标为 [2,5)的元素 //var sli1 []int = arr[2:5] //第二种方法 sli := arr[1:3] //输出数组 fmt.Println("arr", arr) //输出切片 fmt.Println("sli", sli) //输出切片元素个数 fmt.Println("sli", len(sli)) //输出切片容量,切片的容量是是动态变化的 fmt.Println("sli", cap(sli)) }
2、方式二:通过make 内置函数,make函数可以分配并初始化一个类型为切片、映射或通道的对象。
通过make函数创建的切片对应的数组是由切片底层维护,对外不可见
切片名称 := make([]切片类型, 切片长度, 切片容量)
package main import "fmt" func main() { slice := make([]int, 5, 20) fmt.Println(slice) fmt.Println("切片的长度:", len(slice)) fmt.Println("切片的容量:", cap(slice)) }
3、方式三:定义切片,直接指定数组,使用原理类似于make方式
package main import "fmt" func main() { slice := []int{1,4,6} fmt.Println(slice) fmt.Println("切片的长度:", len(slice)) fmt.Println("切片的容量:", cap(slice)) }
三、切片的遍历
package main import "fmt" func main() { var arr [4]int = [4]int{3,6,8,4} //sli := arr[1:3] //如果切取数组当中所有的元素,可以简写为 sli := arr[:] for i := 0; i < len(sli); i++ { } fmt.Println(sli) fmt.Println("==============================================") for key,value := range sli { fmt.Printf("sli[%v]=%v\n", key, value) } }
四、切片的扩容
package main import "fmt" func main() { slice := []int{1,4,6} fmt.Println(slice) //对切片扩容会新创建一个数组,将老数组元素复制到新数组中,在新数组中追加元素 slice2 := append(slice,6,10) fmt.Println(slice2) //如对 原切片进行操作,可以直接利用 append 函数进行修饰 slice = append(slice,6,10) fmt.Println(slice) slice3 := []int{22,44} //利用append 拼接切片 slice = append(slice,slice3...) fmt.Println(slice) }
五、切片的拷贝
package main import "fmt" func main() { var intSli = []int{1, 2, 3, 4, 5} var intSli1 = make([]int, 10) copy(intSli1, intSli) fmt.Println("intSli=", intSli) fmt.Println("intSli1=", intSli1) }
copy(para1, para2) 参数的数据类型是切片
intSli 与 inSli1 的数据空间是相互独立的
六、string 和 slice
string底层是一个byte 数组,因此string 也可以进行切片处理
package main import "fmt" func main() { str := "[email protected]" sli := str[4:] fmt.Println("sli=", sli) }
string是不可变的
package main import "fmt" func main() { str := "[email protected]" sli := str[4:] fmt.Println("sli=", sli) //如果需要修改 string 类型,可以先将 string类型转换为 byte 或者 rune 后进行修改,修改完成后在转换为 string //将 str 转换为 byte 类型可以处理数字及英文 sli1 := []byte(str) sli1[0] = 'w' str = string(sli1) fmt.Println("str=", str) //将 str 转换为 rune类型,可以处理汉字 sli2 := []rune(str) sli2[0] = '北' str = string(sli2) fmt.Println(str) }
七、切片应用示例
编写一个函数可以接收一个 n int,能够将斐波那契的数列放到切片中
package main import "fmt" func fbn(n int)([]uint64) { //声明一个切片 大小为n fbnSlice := make([]uint64, n) //第一个与第二个斐波那契数为1 fbnSlice[0] = 1 fbnSlice[1] = 1 //利用for 循环来生成斐波那契数 for i := 2; i < len(fbnSlice); i++ { fbnSlice[i] = fbnSlice[i - 1] + fbnSlice[i - 2] } return fbnSlice } func main() { fbnSlice := fbn(20) fmt.Println(fbnSlice) }