Golang基础
第四章:流程控制
一、流程控制作用
用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的模块
二、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 := "zwlm@163.com"
sli := str[4:]
fmt.Println("sli=", sli)
}
string是不可变的
package main
import "fmt"
func main() {
str := "zwlm@163.com"
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)
}