Go语言基础 函数

函数

介绍

为完成某一功能的程序指令(语句)的集合 称为函数
在Go 中 函数分为:自定义函数 系统函数

基本语法

基本语法

func函数名(形参列表)(返回值列表){
执行语句…
return返回值列表
}

1)形参列表:表示函数的输入
2)函数中的语句:表示为了实现某一功能代码块3)函数可以有返回值,也可以没有

案例
package main

import "fmt"

func main() {
	//	编写一个简单的计算器
	f := jisuan(1, 1, '/')
	fmt.Println(f)
}
func jisuan(n1 float64, n2 float64, fuhao byte) float64 {
	var res float64
	switch fuhao {
	case '+':
		res = n1 + n2
	case '-':
		res = n1 - n2
	case '*':
		res = n1 * n2
	case '/':
		res = n1 / n2

	}
	return res
}

包的引出

1)在实际的开发中,我们往往需要在不同的文件中,去调用其它文件的定义的函数,比如main.go中,去使用utils.go文件中的函数,如何实现?-》包
2)现在有两个程序员共同开发一个Go项目,程序员xiaoming希望定义函数Cal ,程序员xiaoqiang也,想定义函数也叫Cal。两个程序员为此还吵了起来怎么办?-》包

包的三大作用

1)区分相同名字的函数、变量等标识符
2)当程序文件很多时,可以很好的管理项目
3)控制函数、变量等访问范围,即作用域

包使用的细节

1)在给一个文件打包时,该包对应一个文件夹,
比如这里的utils文件夹对应的向名计是utilc· coomcr文件的包名通常和文件所在的立杜屯夕·wc—般为小写字母。

2)当一个文件要使用其它包函数或变量时,需要先引入对应的包1)引入方式1: import “包名”
2)引入方式2:
package mainimaport (
import (
“fmt-
“包名”
“go_codc/ projcct/utils””
“包名”
3) package指令在文件第一行,然后是import指令。
4)在import包时,路径从$GOPATH的 src下开始,不用带src ,编译器会自动从src下开始引入

函数的调用机制

在这里插入图片描述

函数的递归调用

func main() {
		编写一个简单的计算器
	//f := util.Jisuan(1, 1, '/')

	fmt.Println(f)
	//getsum := Getsum(1, 1)
	//fmt.Println(getsum)
	test(4)
}
// 递归
func test(n1 int) {
	if n1 > 2 {
		n1--
		test(n1)
	}
	fmt.Println("n1=", n1)
}

结果
在这里插入图片描述

分析图

在这里插入图片描述

函数递归需要遵守的重要原则:

1)执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)2)函数的局部变量是独立的,不会相互影响
3)递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)
4)当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回
给谁。同时该函数执行完毕之后或者返回时 也会被销毁

递归调用的案例

请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13…给你一个整数n,求出它的斐波那契数是多少?

// 请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出它的斐波那契数是多少?
func fb(n1 int) int {
	if n1 == 1 || n1 == 2 {
		return 1
	} else {
		return fb(n1-1) + fb(n1-2)
	}
}

函数的使用细节及说明

1)函数的形参列表可以是多个,返回值列表也可以是多个。2)形参列表和返回值列表的数据类型可以是值类型和引用类型。
3)函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似public,首字母小写,只能被本包文件使用,其它包文件不能使用,类似privat
4)函数中的变量是局部的,函数外不生效
5)基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影
响到原来的值
6)如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以
指针的方式操作变量。从效果上看类似引用
7)Go函数不支持重载。

init 函数

介绍

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。

案例

package main

import "fmt"

func init() {
	fmt.Println("init()...")
}
func main() {
	fmt.Println("main().....")
}

D:\ruanjian\go\go1.19.1.windows-amd64\go\bin\go.exe build -o C:\Users\86152\AppData\Local\Temp\GoLand\___go_build_gopl_Demo_initdemo.exe gopl/Demo/initdemo #gosetup
C:\Users\86152\AppData\Local\Temp\GoLand\___go_build_gopl_Demo_initdemo.exe
init()...
main().....


init 函数的注意事项跟细节

当有全局变量跟main函数 init 函数 同时出现的时候 执行的顺序是 全局函数==》init函数==》main函数

案例
package main

import "fmt"

var age = test()

func test() int {
	n1 := 90
	fmt.Println("全局变量。。。")
	return n1
}

func init() {
	fmt.Println("init()...")
}
func main() {
	fmt.Println("main().....", age)
}
D:\ruanjian\go\go1.19.1.windows-amd64\go\bin\go.exe build -o C:\Users\86152\AppData\Local\Temp\GoLand\___go_build_gopl_Demo_initdemo.exe gopl/Demo/initdemo #gosetup
C:\Users\86152\AppData\Local\Temp\GoLand\___go_build_gopl_Demo_initdemo.exe
全局变量。。。
init()...
main()..... 90



面试题

如果在main.go文件中引入了其他的文件的 两个文件中都有init函数跟全局变量 那么执行的顺序是什么?
在这里插入图片描述

匿名函数

介绍

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

func main() {
	res := func(n1 int, n2 int) int {
		return n1 + n2
	}(10, 20)
	fmt.Println(res)
}

闭包

介绍

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

函数 Defer

为什么需要 defer

在函数中 经常用于创建资源(比如 数据库的链接 文件句柄 锁 等)为了在函数执行完毕后 及时的释放资源 Go 设计者提供 defer (延时机制)

package main

import "fmt"

// 测试 defer

func sum(n1 int, n2 int) int {
	// defer 会在函数执行完成之后 执行 写了 defer 后 默认进入一个单独的栈中 最后先入后出
	defer fmt.Println("n1=", n1)
	defer fmt.Println("n2=", n2)
	res := n1 + n2
	fmt.Println(res)
	return res
}
func main() {
	i := sum(10, 20)
	fmt.Println(i)
}

执行结果

在这里插入图片描述

defer 的细节说明

1,当执行到一个defer时 不会立即执行defer 后的语句 而是将defer 后的语句压入到一个栈中 然后继续执行 先入后出
2,在defer 将语句放入到栈时 也会将相关拷贝同时入栈
代码

package main

import "fmt"

// 测试 defer

func sum(n1 int, n2 int) int {
	// defer 会在函数执行完成之后 执行 写了 defer 后 默认进入一个单独的栈中 最后先入后出
	defer fmt.Println("n1=", n1)
	defer fmt.Println("n2=", n2)
	// 这里加加的时候 此时 n1 的值是 11 n2 是 21  但是当我们输出的时候 
	n1++
	n2++
	res := n1 + n2
	fmt.Println(res)
	return res
}
func main() {
	i := sum(10, 20)
	fmt.Println(i)
}

在这里插入图片描述

最佳实践

在链接数据库或者其他的时候用来释放资源、

函数参数的传递方式

基本介绍

值类型参数默认就是值传递 而引用类型参数默认就是引用传递

两种的传递方式

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

String 函数的使用 (字符串中常用的函数)

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// 字符串长度
	str := "冯娇娇是美女"
	fmt.Println(len(str))
	// 遍历字符串
	str1 := "hahahhaha娇娇"
	r := []rune(str1)
	for i := 0; i < len(r); i++ {
		fmt.Printf("i=%c\n", r[i])
	}
	// 字符串转换 整数
	str2 := "111"
	atoi, _ := strconv.Atoi(str2)
	fmt.Printf("atoi 的类型 %T\n", atoi)
	// 整数转换字符串
	age := 19
	itoa := strconv.Itoa(age)
	fmt.Printf("itoa 的类型 %T \n", itoa)
	// 字符串转换 byte
	bytes := []byte("fengjiaojiao")
	fmt.Printf("bytes 的类型 %T \n", bytes)
	// byte 转换字符串
	str = string([]byte{11, 55, 99})
	fmt.Printf("str 的类型 %T \n", str)
	// 查找子串在不在字符串中
	contains := strings.Contains("fengjiaojiao", "f")
	fmt.Printf("contains %v \n", contains)
	// 查看出现的次数
	count := strings.Count("jiaojiao", "j")
	fmt.Printf("count %v \n", count)
	// 比较不区分大小写的 字符串 (== 是区分大小写的)
	fold := strings.EqualFold("abc", "ABC")
	fmt.Printf("fold=%v \n", fold)
	// index 第一次出现 index 的时候的值
	index := strings.Index("kiko", "k")
	fmt.Printf("index= %v \n", index)
	// 最后一次出现的位置
	index1 := strings.LastIndex("kiko", "o")
	fmt.Printf("index1= %v \n", index1)
	// 将指定的子串替换成 另外一个子串 最后一个-1 表示全部替换的意思
	replace := strings.Replace("goland", "go", "kiko", -1)
	fmt.Printf("replace= %v \n", replace)
	// 按照指定的某个字符 分割 拆成字符串数组
	split := strings.Split("f e n g j i a o j i a o", " ")
	for i := range split {
		fmt.Println(split[i])
	}
	// 大小写字母的转换
	strings.ToLower("Go")
	strings.ToUpper("kiO")
	// 将字符串左右俩边的空格去掉
	strings.TrimSpace(" kiko ")
	// 将字符串左右两边指定的字符去掉
	strings.TrimLeft("!bbb", "!")
	strings.TrimRight("!bbb", "!")
	// 判断字符串是否以指定的字符串开头或结束
	strings.HasPrefix("http", "h")
	strings.HasSuffix("http", "h")

}

时间和日期函数

需要导入 time 包

package main

import (
	"fmt"
	"time"
)

func main() {
	// 获取当前时间
	now := time.Now()
	fmt.Printf("now=%v now type=%T", now, now)
	// 通过 time 获取 年月日时分秒
	fmt.Printf("%v \n", time.Now().Year())
	fmt.Printf("%v \n", int(time.Now().Month()))
	fmt.Printf("%v \n", time.Now().Day())
	fmt.Printf("%v \n", time.Now().Hour())
	fmt.Printf("%v \n", time.Now().Minute())
	fmt.Printf("%v \n", time.Now().Second())
	// 格式化时期和时间 2006/01/02 15:04:05 时间是开发者定的
	fmt.Printf("%v \n", now.Format("2006/01/02 15:04:05"))
	// 时间的常量类
	for i := 0; i < 10; i++ {
		fmt.Print(i)
		time.Sleep(time.Second)
	}
}

go内置函数

1 len 用来求长度 比如 string array slice map channel
2 new 用来分配内存 主要用来分配值类型 比如 int float32 struct 返回的是指针
3 make 用来分配内存 主要用来分配引用类型 比如 chan map slice

错误处理机制

基本说明

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

import "fmt"

func main() {
	test()
	fmt.Println("ddddd")
}
func test() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println(err)
		}
	}()
	num := 10
	num1 := 0
	res := num / num1
	fmt.Println(res)
}

自定义错误

func readConf(name string)(err error)  {
	if name=="fff" {
		// 这里就是读取...
		return nil
	} else {
		return errors.New("读取失败")
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值