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 := "[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)
}
Posted in Go