【Golang第4章:函数】Golang包的引用,return语句、指针、匿名函数、闭包、go函数参数传递方式,golang获取当前时间

介绍

本人是个菜鸡,这个是在B站上看边看视频边做的笔记,这一章是编程语言的函数

Golang包的引用,return语句、指针、匿名函数、闭包、go函数参数传递方式,golang获取当前时间,具体请看【文章目录】

配套视频自己去B站里面搜【go语言】,最高的播放量就是

里面的注释我写的可能不太对,欢迎大佬们指出╰(°▽°)╯

(四).函数

函数定义:

func 函数名 (参数列表) (返回值列表) { //返回值只有一个时可以不写()

//函数体

return 返回值列表

}

例子:

package main

import "fmt"

//声明一个函数为cal,声明3个值
func cal(n1 float64, n2 float64, n3 byte) float64 {
	var res float64
	switch n3 {
	case '+':
		res = n1 + n2
	case '-':
		res = n1 - n2
	case '*':
		res = n1 * n2
	case '/':
		res = n1 / n2
	default:
		fmt.Println("符合出入错误。。。")
	}
	return res
}

func main() {
	var n1 float64 = 4.5
	var n2 float64 = 2.5
	var n3 byte = '+'
	//声明一个n4并把函数的结果赋值,调用函数
	n4 := cal(n1, n2, n3)
	fmt.Println("结果是:", n4)
}

1.包的引入

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gktLAhmh-1669103278411)(assets/image-20220808204734406.png)]

package 指令在 文件第一行,然后是 import 指令。

在 import 包时,路径从 $GOPATH 的 src 下开始,不用带 src , 编译器会自动从 src 下开始引入


引入包并取别名,注意细节:取别名后,原来的包名就不能使用了

package utils
import "fmt"
var Num1 int = 300


package main
import (
	u111 "demo13/utils"  //取别名
	"fmt"
)
func main() {
	fmt.Println("数字是:", u111.Num1) //引入变量
}

2.项目打包成可执行文件

默认打包路径在$GOPATH目录下

PS D:\code\Go> go build 08demo/main
PS D:\code\Go> dir


    目录: D:\code\Go


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----          2022/8/9     14:26                pkg
d-----          2022/8/9     14:26                src
-a----         2022/8/10     10:19        1890816 main.exe

指定打包到当前bin目录下

PS D:\code\Go> go build -o ./bin/my.exe 08demo/main
PS D:\code\Go> dir

    目录: D:\code\Go

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2022/8/10     10:21                bin
d-----          2022/8/9     14:26                pkg
d-----          2022/8/9     14:26                src
-a----         2022/8/10     10:19        1890816 main.exe

3.return语句

基本语法和说明:

Go函数支持返回多个值,这一点时其他编程语言没有的。

func 函数名 (形参列表) (返回值类型列表) {
    语句...
    return 返回值列表
}
  1. 如果返回多个值时,在接收时,希望忽略某个返回值,则使用下划线_符合表示占位忽略
  2. 如果返回值只有一个,(返回值类型列表) 可以不写()

返回一个值

package main

import "fmt"

func test(n1 int, n2 int) int { //返回参数时,需要声明返回的数值类型
	sum := n1 + n2
	fmt.Println("test sum = ", sum)
	//当函数有return的语句时,就是将结果返回给调用者
	//即谁调用我,我就返回给谁
	return sum
}

func main() {
	//调用test
	sum := test(10, 20)
	fmt.Println("main sum = ", sum)
}

返回多个值

func test(n1 int, n2 int) (int, int) { //返回多个参数
	sum := n1 + n2
	sub := n1 - n2

	return sum, sub
}

func main() {
	//调用test,调用2个返回参数
	sum1, sub1 := test(10, 20)
	fmt.Printf("sum=%v,sub=%v\n", sum1, sub1)

	//如果只返回一个参数,只要sum2,可以使用占位符下划线 _ 替代 sub2
	sum2, _ := test(10, 20)
	fmt.Println("sum=", sum2)
    
}

4.递归调用

基本介绍

一个函数在函数体内调用了本身,我们称为递归调用

func test(n int) {
	if n > 2 {
		n--
		test(n)
	}
	fmt.Println("n=", n)
}

func main() {
	test(4)
}

在这里插入图片描述

5.函数细节

使用指针修改函数外数据

func test(n1 *int) { //2.取内存地址原值
	*n1 = *n1 + 10 //3.取内存地址原值并进行运算
	fmt.Println("test  n1=", *n1)
}

func main() {
	num := 20
	fmt.Println("main num=", num)
	test(&num) //1.传入内存地址
	fmt.Println("main num=", num) //4.由于内存地址里面的值被修改,输出30
}

结果:

main num= 20
test  n1= 30
main num= 30

通过变量对函数进行调用

//在GO中,函数也是一种数据类型
//可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用
func getSum(n1 int, n2 int) int {
	return n1 + n2
}

func main() {
	a := getSum
	fmt.Printf("a的类型%T, getSum类型是%T\n", a, getSum)

	res := a(10, 20) //等价于 res := getSum(10, 20)
	fmt.Println("res=", res)
}

函数回调

函数既然是一种数据类型,因此在 Go 中,函数可以作为形参,并且调用

func getSum(n1 int, n2 int) int { //4.取到对应的参,运算后并进行返回
	return n1 + n2
}

//2.接受参数,aaa表示为getSumF函数的变量,num1和num2接收后分别为20和30
func myFun(aaa func(int, int) int, num1 int, num2 int) int {
	return aaa(num1, num2) //3.返回num1和num2到aaa变量 //5.返回运算后结果
}

func main() {
	res := myFun(getSum, 20, 30) //1.把参数返回给函数myFun
	fmt.Println("res=", res)     //6.完成输出
}

使用type取别名

func main() {
	type myInt int //给int取别名

	var num1 myInt //虽然都是int类型,但是go认为myInt和int是2个类型
	num1 = 40
	num2 := int(num1) //这里go认为myInt和int是2个数据类型
	fmt.Println("num1=", num1, "num2=", num2)
}

支持可变参数

//支持0到多个参数
func sum (args ...int) sum int{
}
//支持1到多个参数
func sum (n1 int, args ...int) sum int{
}

如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。

6.init函数

每一个源文件中都有一个init函数,最大的作用是用来初始化源文件,该函数会在main函数执行前被调用

func main() { //3.在执行main
	fmt.Println("main...", age)
}

func init() { //2.在执行init
	fmt.Println("init...")
}

var age = test() //1.根据规则先执行变量

func test() int { //让age优先调用
	fmt.Println("test...")
	return 90
}

输出:

$ go run .\main.go
test...
init...
main... 90

如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程是全局变量定义->init 函数->main 函数

如果main函数引入的utils文件里面有init函数,优先执行被引入的init函数

7.匿名函数

Go 支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考 虑使用匿名函数,匿名函数也可以实现多次调用。

//全局匿名函数Fun1
//如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效

var Fun1 = func(n1 int, n2 int) int {
	return n1 * n2
}

func main() {
	//在定义匿名函数是时就直接调用,这种方式匿名函数只能调用一次
	res1 := func(n1 int, n2 int) int { //没有函数名
		return n1 + n2
	}(10, 20) //将参数返回给n1和n2

	fmt.Println("res1=", res1)

	//可以调用多次
	a := func(n1 int, n2 int) int {
		return n1 - n2
	}
	res2 := a(10, 30) 
	fmt.Println("res2=", res2)

	res3 := a(20, 10)
	fmt.Println("res3=", res3)
    
    res4 := Fun1(3, 4) //调用全局匿名函数
	fmt.Println("res4=", res4)
}

8.闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

func AddUpper() func(int) int { //闭包
	var n int = 10 //累加器
	return func(i int) int {
		n = n + i //进行累加
		return n //把值返回给累加器
	}
}

func main() {
	//使用前面的代码
	f := AddUpper()
	fmt.Println(f(1)) //10+1,结果为11,现在累加器变量n为11
	fmt.Println(f(2)) //11+2,结果为13,现在累加器变量n为13
 	fmt.Println(f(1)) //13+1,结果为14
}
import (
	"fmt"
	"strings"
)

/*
1) 编写一个函数 makeSuffix(suffix string) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包
2) 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg 后缀,则返回原文件名。
3) 要求使用闭包的方式完成
4) strings.HasSuffix , 该函数可以判断某个字符串是否有指定的后缀。
*/

func makeSuffix(suffix string) func(string) string {
	return func(Name string) string {
		if !strings.HasSuffix(Name, suffix) {
			return Name + suffix
		}
		return Name
	}
}

func main() {
	str1 := makeSuffix(".jpg")
	fmt.Println("文件名处理后=", str1("winter")) //winter.jpg
	fmt.Println("文件名处理后=", str1("win.jpg")) //win.jpg
	fmt.Println("文件名处理后=", str1("win.png")) //win.png.jpg
}

上面代码的总结和说明:

  1. 返回的匿名函数和 makeSuffix (suffix string) 的 suffix 变量 组合成一个闭包,因为 返回的函数引用 到 suffix 这个变量
  2. 我们体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每 次都传入 后缀名,比如 .jpg ,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复 使用。大家可以仔细的体会一把!

9.defer

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完 毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。

func sun(n1 int, n2 int) int { //2.传入参数
	//当执行到defer是,会将defer后面的语句按顺序存放到独立的栈(defer栈)中
	//当程序执行完毕后,再从defer栈,按照先入后出的方式出栈
	//入栈(defer栈)时,会同时拷贝n1和n2的值,后面参数变量不会影响已入栈的参数
	defer fmt.Println("ok1 n1=", n1) //3.因为有defer,第1个入栈,暂时不执行  //9.继续出栈,在执行ok1
	defer fmt.Println("ok2 n2=", n2) //4.因为有defer,第2个入栈,暂时不执行  //8.进行出栈,先执行ok2
	n1++
	n2++
	res := n1 + n2               //5.相加后赋值给res
	fmt.Println("ok3 res=", res) //6.先输出ok3
	return res                   //7.当sun函数执行完毕,返回参数后,在执行独立的栈(defer)的代码
}

func main() {
	res := sun(10, 20)       //1.赋值给res,调用sun
	fmt.Println("res=", res) //10.输出结果
}

当执行到defer是,会将defer后面的语句按顺序存放到独立的栈(defer栈)中

当程序执行完毕后,再从defer栈,按照先入后出的方式出栈

入栈(defer栈)时,会同时拷贝n1和n2的值,后面参数变量不会影响已入栈的参数

defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源

  1. 在 golang 编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是 锁资源), 可以执行 defer file.Close() defer connect.Close()
  1. 在 defer 后,可以继续使用创建资源.
  2. 当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源.
  3. 这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦

10.函数参数传递方式

1)基本介绍

我们在讲解函数注意事项和使用细节时,已经讲过值类型和引用类型了,这里我们再系统总结一 下,因为这是重难点,值类型参数默认就是值传递,而引用类型参数默认就是引用传递。

2)两种传递方式

  1. 值传递
  2. 引用传递

其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的 拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的 数据大小,数据越大,效率越低。

3)值类型和引用类型

  1. **值类型:**基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct
  2. **引用类型:**指针、slice 切片、map、管道 chan、interface 等都是引用类型

4)值传递和引用传递使用特点

1.**值类型默认是值传递:**变量直接存储值,内存通常在栈中分配

在这里插入图片描述

2.**引用类型默认是引用传递:**变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。

在这里插入图片描述

3.如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操 作变量。从效果上看类似引用 。这个案例在前面详解函数使用注意事项的


5)变量作用域

1.函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部

在这里插入图片描述

2.函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用 域在整个程序有效

在这里插入图片描述

3.如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块

在这里插入图片描述


练习1
var name = "tom" //全局变量

func test01() {
	fmt.Println(name)
}

func test02() {
	name := "jack" // name := "jack" 是重新声明局部变量 等同与 var name = "jack" ,如果是name = "jack",相对于赋值jack给name
	fmt.Println(name)
}

func main() {
	fmt.Println(name) //tom
	test01()          //tom
	test02()          //jack
	test01()          //tom
}

name := "jack":重新声明局部变量 等同与 var name string name = "jack"

name = "jack":相当于赋值jack给name的全局变量,修改了全局变量,属于赋值语句


练习2

在这里插入图片描述


6)字符串常用的系统函数

说明:字符串在我们程序开发中,使用的是非常多的,常用的函数需要同学们掌握[带看手册或者 官方编程指南]:

1.统计字符串的长度,按字节 len(str)

func main() {
	//golang的编码统一为utf-8(ascii的字符(字母和数字)占一个字节,汉字占用3个字节)
	str := "hello你好"
	fmt.Println("hello你好字节数量为:", len(str))
}

2.字符串遍历,同时处理有中文的问题 r := []rune(str)

	str := "hello你好"
	str1 := []rune(str)
	for i := 0; i < len(str1); i++ {
		fmt.Printf("字符=%c\n", str1[i])
	}

3.字符串转整数: n, err := strconv.Atoi("12")

    //字符串转整数,err值必须为数字
	n, err := strconv.Atoi("1设置")
	//判断n是否为数字,nil是一种空类型表示为0值
	if err != nil {
		fmt.Println("转换错误", err)
	} else {
		fmt.Println("转换成功", n)
	}

4.整数转字符串 str := strconv.Itoa(12345)

	str := strconv.Itoa(1234)
	fmt.Printf("str=%v,str=%T", str, str)

5.字符串 转 []byte: var bytes = []byte("hello go")

	var bytes = []byte("hello go")
	//输出对应字符的ascii编码
	fmt.Printf("byte=%v\n", bytes) //byte=[104 101 108 108 111 32 103 111]

6.[]byte 转 字符串: str = string([]byte{97, 98, 99})

	str := string([]byte{97, 98, 99})
	//输出ascii编码对应的字符
	fmt.Printf("str=%v\n", str) //str=abc

7.10进制转2,8,16进制:str = strconv.FormatInt(123, 2) // 2-> 8 , 16

	//输入int64类型10进制参数,返回2进制
	str := strconv.FormatInt(123, 2)
	fmt.Printf("123对应的2进制是:%v\n", str)

	//输入int64类型10进制参数,返回16进制
	str1 := strconv.FormatInt(123, 16)
	fmt.Printf("123对应的16进制是:%v\n", str1)

8.查找子串是否在指定的字符串中: strings.Contains("seafood", "foo") //true

	//字符串查找
	str := strings.Contains("seafood", "foo")
	str1 := strings.Contains("seafood", "adb")
	fmt.Printf("str=%v\n", str)  //查找到为真true
	fmt.Printf("str=%v\n", str1) //查不到为假false

9.统计一个字符串有几个指定的子串 : strings.Count("ceheese", "e") //4

	//查看字符串里面有几个e,没有返回0
	str := strings.Count("ceheese", "e")
	fmt.Printf("str=%v\n", str) // str=4

10.不区分大小写的字符串比较(==是区分字母大小写的): fmt.Println(strings.EqualFold("abc", "Abc")) // true

	str := strings.EqualFold("abc", "Abc")
	//比较字符串,不区分大小写
	fmt.Printf("结果= %v\n", str) //true

	//比较字符串,区分大小写
	fmt.Println("结果=", "abc" == "Abc") //false

11.返回子串在字符串第一次出现的 index(指数) 值,如果没有返回则-1 : strings.Index("NLT_abc", "abc") // 4

	//abc在字符串NLT_abc第一次出现为第5个字符,返回4
	str := strings.Index("NLT_abc", "abc")
	fmt.Printf("str=%v\n", str)

	//cabc在参数里面没有出现,返回-1
	str1 := strings.Index("NLT_abc", "cabc")
	fmt.Printf("str1=%v\n", str1)

12.返回子串在字符串最后一次出现的index,如没有返回则-1 : strings.LastIndex("go golang", "go")

	//go在字符串go golang第最后一次出现为第4个字符,返回3
	str := strings.LastIndex("go golang", "go")
	fmt.Printf("str=%v\n", str)

13.将指定的子串替换成 另外一个子串: strings.Replace("go go hello", "go", "go 语言", n),n 可以指定你希望替换几个,如果 n=-1 表示全部替换

	//对指定的字符串进行替换
	//第1个为原字符串,第2个为你要替换的字符串,第3个为你替换后的参数,第4个为你要替换几个原字符
	//替换1个go,输出"go go hello"为"你好 go hello"
	//替换2个go,输出"go go hello"为"你好 你好 hello"
	str := strings.Replace("go go hello", "go", "你好", 2)
	fmt.Printf("str=%v\n", str)

14.按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:strings.Split("hello,wrold,ok", ",")

	//按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组
	//以","逗号为分割符,输出"hello wrold ok"
	str := strings.Split("hello,wrold,ok", ",")
	fmt.Printf("str=%v\n", str)

	//查看是不是真的成了数组
	for i := 0; i < len(str); i++ {
		fmt.Printf("str%v=%v\n", i, str[i])
	}

15.将字符串的字母进行大小写的转换: strings.ToLower("Go")strings.ToUpper("Go")

	str := "goLang Hello"
	str1 := strings.ToLower(str) //改为全小写
	str2 := strings.ToUpper(str) //改为全大写
	fmt.Printf("str=%v\n", str1)
	fmt.Printf("str=%v\n", str2)

16.将字符串左右两边的空格去掉:strings.TrimSpace(" tn a lone gopher ntrn ")

	//将字符串左右两边的空格去掉:
	//`strings.TrimSpace(" tn a lone gopher ntrn ")`
	str1 := strings.TrimSpace(" tn a lone gopher ntrn ")
	fmt.Printf("str=%q\n", str1)

17.将字符串左右两边指定的字符去掉 :strings.Trim("! hello! ", "! ") 将左右两边 "! " 叹号和空格去掉

	//将字符串左右两边指定的字符去掉
	//:strings.Trim("! hello! ", "! ")
	//将左右两边 "! " 叹号空格去掉
	str := strings.Trim("! hello! ", "! ")
	str1 := strings.Trim("! hello! ", " ")
	fmt.Printf("str=%q\n", str)
	fmt.Printf("str=%q\n", str1)

18.将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! ", " !") // [“hello”] //将左边 ! 和 ""去掉

19.将字符串右边指定的字符去掉 :strings.TrimRight("! hello! ", " !") // [“hello”] //将右边 ! 和 ""去掉

20.判断字符串是否以指定的字符串开头: strings.HasPrefix("ftp://192.168.10.1", "ftp") // true

21.判断字符串是否以指定的字符串结束: strings.HasSuffix("NLT_abc.jpg", "abc") //false

7)时间和日期相关函数

基本的介绍

说明:在编程中,程序员会经常使用到日期相关的函数,比如:统计某段代码执行花费的时间等。

①.获取当前系统时间和日期
func main() {
	//获取当前时间
	now := time.Now()
	fmt.Printf("now=%v\nnow=%T\n", now, now)

	fmt.Printf("年%v\n", now.Year())
	fmt.Printf("月%v\n", now.Month())
	fmt.Printf("月%v\n", int(now.Month())) //转换为int类型
	fmt.Printf("日%v\n", now.Day())
	fmt.Printf("时%v\n", now.Hour())
	fmt.Printf("分%v\n", now.Minute())
	fmt.Printf("秒%v\n", now.Second())

	//格式化日期时间1
	fmt.Printf("当前时间为:%02d-%02d-%02d %02d:%02d:%02d\n",
		now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second())

	datestr := fmt.Sprintf("当前时间为:%02d-%02d-%02d %02d:%02d:%02d\n",
		now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second())
	//输出赋值后的时间
	fmt.Printf("%v\n", datestr)

	//格式化日期时间2
	fmt.Println(now.Format("2006-01-02 15:04:05"))
	fmt.Println("年=", now.Format("2006"))
	fmt.Println("日=", now.Format("02"))
}

对上面代码的说明:

“2006/01/02 15:04:05” 这个字符串的各个数字是固定的,必须是这样写。

“2006/01/02 15:04:05” 这个字符串各个数字可以自由的组合,这样可以按程序需求来返回时间

②.时间的常量
const (
Nanosecond Duration = 1 //纳秒
Microsecond = 1000 * Nanosecond //微秒
Millisecond = 1000 * Microsecond //毫秒
Second = 1000 * Millisecond //秒
Minute = 60 * Second //分钟
Hour = 60 * Minute //小时
)
	//需求,每隔1秒打印一个数字,打印到100时就退出
	//需求2,每隔0.1秒打印一个数字,打印到100时就退出
	i := 0
	for {
		i++
		fmt.Println(i)
		//等待1秒
		//time.Sleep(time.Second)

		//等待0.1秒  1000毫秒 * 100 = 0.1秒
		time.Sleep(time.Millisecond * 100)

		if i == 100 {
			break
		}
	}
③.使用时间戳获取随机数字

Unix时间戳和UnixNano时间戳

在这里插入图片描述

	now := time.Now()
	fmt.Println("Unix时间戳=", now.Unix())
	fmt.Println("UnixNano时间戳=", now.UnixNano())

练习1

编写一段代码来统计 函数 test03 执行的时间

package main

import (
	"fmt"
	"strconv"
	"time"
)

func test03() {
	str := ""
	for i := 0; i < 100000; i++ { //运行10万次
		str += "hello" + strconv.Itoa(i) //拼接字符
	}
}

func main() {
	start := time.Now().Unix()
	fmt.Println(start)
	test03()
	end := time.Now().Unix()
	fmt.Println(end)
	fmt.Printf("执行test03耗费时间为%v秒\n", end-start)
}


8)内置函数

说明: Golang 设计者为了编程方便,提供了一些函数,这些函数可以直接使用,我们称为 Go 的内置函 数。文档:https://studygolang.com/pkgdoc -> builti

1.len:用来求长度,比如 string、array、slice、map、channel

2.new:用来分配内存,主要用来分配值类型,比如 int、float32,struct…返回的是指针

func main() {
	num1 := 100
	fmt.Printf("类型=%v,值=%v,地址=%v\n", num1, num1, &num1)

	num2 := new(int) //给num2这个指针指定一个空间,类型为int,默认值为0,返回的是指针
	fmt.Printf("类型=%T , 值=%v , 地址=%v , num2这个指针指向的值=%v\n", num2, num2, &num2, *num2)

	*num2 = 100 //给num2赋值
	fmt.Printf("类型=%T , 值=%v , 地址=%v , num2这个指针指向的值=%v\n", num2, num2, &num2, *num2)

	*num2 = 200 //给num2赋值
	fmt.Printf("类型=%T , 值=%v , 地址=%v , num2这个指针指向的值=%v\n", num2, num2, &num2, *num2)
}
PS D:\Code\Golang\src\demo15\main> go run .\main.go
类型=100,值=100,地址=0xc00012a058
类型=*int , 值=0xc00012a078 , 地址=0xc00014e020 , num2这个指针指向的值=0
类型=*int , 值=0xc00012a078 , 地址=0xc00014e020 , num2这个指针指向的值=100
类型=*int , 值=0xc00012a078 , 地址=0xc00014e020 , num2这个指针指向的值=200

3.make:用来分配内存,主要用来分配引用类型,比如 channel、map、slice。这个我们后面讲解。

9)错误处理

func test() {
	num1 := 10
	num2 := 0
	res := num1 / num2 //错误,0不能作为分母
	fmt.Println(res)
}

func main() {
	test()
}

对上面代码的总结

  1. 在默认情况下,当发生错误后(panic) ,程序就会退出(崩溃.)
  1. 如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行。还可 以在捕获到错误后,给管理员一个提示(邮件,短信。。。)

这里引出我们要将的错误处理机制

基本说明
  1. Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try…catch…finally 这种处理。
  2. Go 中引入的处理方式为:defer,panic,recover
  3. 这几个异常的使用场景可以这么简单描述:Go 中可以抛出一个 panic 的异常,然后在 defer 中 通过 recover 捕获这个异常,然后正常处理

使用 defer+recover 来处理错误

package main

import (
	"fmt"
	"time"
)

func test() {
	//使用defer + recover来捕获和处理异常
	defer func() { //匿名函数
		err := recover() // recover()是一个内置函数,可以捕获异常
		if err != nil {  //判断err是否为空,
			fmt.Println("err=", err)
		}
	}() // () 调用匿名函数

	num1 := 10
	num2 := 0
	res := num1 / num2 //错误,0不能作为分母
	fmt.Println(res)
}

func main() {
	test()
	for {
		fmt.Println("继续执行代码")
		time.Sleep(time.Second) //休眠
	}
}

错误处理的好处

进行错误处理后,程序不会轻易挂掉,如果加入预警代码,就可以让程序更加的灵活

自定义错误

Go 程序中,也支持自定义错误, 使用 errors.New 和 panic 内置函数

  1. errors.New(“错误说明”) , 会返回一个 error 类型的值,表示一个错误

  2. panic 内置函数 ,接收一个 interface{}类型的值(也就是任何值了)作为参数。可以接收 error 类型的变量,输出错误信息,并退出程序

//函数去读取一个配置文件名
//如果文件名传入不正确,返回一个自定义错误
func readConf(name string) (err error) {
	if name == "config.ini" { //判断config.ini文件
		return nil //当文件名正确时是返回一个空
	} else {
		return errors.New("未找到config.int文件") //当文件名错误时返回一个自定义错误
	}
}

func test01() {
	err := readConf("config.i i") //传入参数给readConf函数
	if err != nil {
		panic(err) // 如果读取文件发生错误,就输出这个错误,并终止程序
	}
	fmt.Println("程序继续执行")
}

func main() {
	test01()
}

11.函数练习


练习1

循环打印输入的月份的天数。【使用continue实现】
— 要有判断输入的月份是否错误的语句

效果:

在这里插入图片描述


练习2

编写一个函数:
随机猜数游戏:
随机生成一一个1- - 100 的整数
有十次机会
如果第一次就猜中,提示“你真是个天才”
如果第2–3次猜中,提示“你很聪明,赶上我了”
如果第4–9次猜中,提示“一般般’
如果最后一次猜中,提示“可算猜对啦”

一次都没猜对, 提示“说你点啥好呢”


练习3

编写一个函数:输出100以内的所有素数(素数就只能被1和本身整除的数),每行显示5个;并求和


练习4

编写-一个函数,判断是打鱼还是晒网.

中国有句俗语叫“三天打鱼两天晒网”。如果从1990年1月1日起开始执行“三天打鱼两天晒网”。如何判断在以后的某-天中是“打鱼”还是“晒网”?


练习5

打印如下效果

----------小小计算器----------

1.加法
2.减法
3.乘法
4.除法
0.退出
请选择:
10+5=15

----------小小计算器----------

1.加法
2.减法
3.乘法
4.除法
o.退出
请选择: 0
程序退出!


练习6

输出小写的a-z以及大写的Z-A,
使用for和ASCII码


章节目录

【Golang第1~3章:基础】如何安装golang、第一个GO程序、golang的基础

【Golang第4章:函数】Golang包的引用,return语句、指针、匿名函数、闭包、go函数参数传递方式,golang获取当前时间

【Golang第5章:数组与切片】golang如何使用数组、数组的遍历和、使用细节和内存中的布局;golang如何使用切片,切片在内存中的布局

【Golang第6章:排序和查找】golang怎么排序,golang的顺序查找和二分查找,go语言中顺序查找二分查找介绍和案例

【Golang第7章:map】go语言中map的基本介绍,golang中map的使用案例,go语言中map的增删改查操作,go语言对map的值进行排序

【Golang第8章:面向对象编程】Go语言的结构体是什么,怎么声明;Golang方法的调用和声明;go语言面向对象实例,go语言工厂模式;golang面向对象的三大特性:继承、封装、多态

【Golang第9章:项目练习】go项目练习家庭收支记账软件项目、go项目练习客户管理系统项目

【Golang第10章:文件操作】GO语言的文件管理,go语言读文件和写文件、GO语言拷贝文件、GO语言判断文件是否存在、GO语言Json文件格式和解析

【Golang第11章:单元测试】GO语言单元测试

【Golang第12章:goroutine协程与channel管道】GO语言goroutine协程和channel管道的基本介绍、goroutine协

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值