golang函数使用基础
函数
介绍
有其他语言基础的话就没啥介绍的,基本语法:
func functionName(形参列表) (返回值列表){
执行语句
return 返回值列表
}
例子:
func getSum(n1 int, n2 int) int {
var res int = n1 +n2
return res
}
包
实际就创建不同的文件夹,存放程序文件。Go的每一个文件都输属于一个包的,go以包的形式来管理文件和目录结构。
作用:
- 区分相同名字的函数与变量符
- 当项目比较复杂时,便于管理项目
- 控制函数、变量的访问范围,也就是作用域
打包指令:package 包名
引入指令:import "包路径"
注意事项:
- 包中的函数与变量如果首字母大写则是对外公开的,称为该函数可以导出,如果小写则是非公开的
- 包名通常应该与所处文件夹保持一致,不一致也可以
- 同一个包下面,不能有相同的函数名
- main包只能有一个
init函数
每一个源文件都可以包含一个init函数,该函数在main函数执行之前被Go运行框架调用。一般完成初始化工作 。
在一个文件中如果同时含有全局变了定义、init函数与main函数,那么执行流程是:
全局变量定义->init函数->main函数
当一个文件引用的包里面有全局变量定义与init函数时,先执行包的全局变量定义与init函数,再执行该文件的全局变量定义与init函数,最后执行main函数
匿名函数
如果希望某一个函数只使用一次,可以考虑匿名函数。当然匿名函数也可以实现多次使用。什么场景下会使用匿名函数呢?也就是闭包。一个最常用的场景就是程序运行出现错误的时候,我们要去恢复,一般是通过关键字defer,我们要有一个程序逻辑,在某些特定场景下运行的,但是我没有必要为整个逻辑定义函数,通常这样就可以使用闭包来做。
- 使用方式1:在定义时就使用
res1 := func (n1 int, n2 int) int {
return n1+n2
}(5, 6)//传入参数
fmt.Printf("res1的值是%d\n", res1) //结果为11
- 使用方式2:将匿名函数赋给一个变量,通过变量调用函数,可以反复调用(用处感觉不是很大,用处是可以在main()中定义函数)
func main(){
a = func (n1 int, n2 int) int { //此时a的数据类型为函数类型
return n1+n2
}
res2 := a(5, 6) //a是变量,不是函数名
fmt.Printf("res2的值是%d\n", res2) //结果为11
res3 := a(10, 6) //a是变量,不是函数名
fmt.Printf("res3的值是%d\n", res3) //结果为16
}
- 使用方式3:全局匿名函数:将匿名函数赋给一个全局变量,通过变量调用函数
闭包
闭包是一个函数和与其相关的引用环境组成的整体。例子:
package main
import (
"fmt"
)
func AddUpper() func (int) int {
var n int = 10
return func(x int) int {
n = n + x;
return n
}
}
func main(){
f := AddUpper()
fmt.Println(f(1)) //结果为11
fmt.Println(f(2)) //结果为13
fmt.Println(f(3)) //结果为16
g := AddUpper()
fmt.Println(g(2)) //结果为12
}
// AddUpper是一个函数,其返回的数据类型是函数类型。
// 返回的匿名函数就是闭包的函数。
// n是引用的环境,构成了一个整体
// 可以理解为闭包是一个类,环境就是成员变量,匿名函数是唯一的一个成员函数。
defer
在函数中需要创建资源(redis,MySQL链接或者文件、锁资源),为了在函数执行完毕后及时的释放资源,Go的设计者提供了defer机制。
package main
import "fmt"
func sum(n1 int, n2 int) int {
// 当执行到defer时,会将defer后面的语句压入到一个独立的栈中,暂时不执行
// 当函数执行完毕后,再从栈中按照先入后出的方式出栈,并一一执行
// 在defer将语句入栈时,也会将语句相关的资源与变量拷贝至栈中,在以下两个语句中分别就是n1, n2
defer fmt.Println("ok1 n1=", n1)
defer fmt.Println("ok2 n1=", n2)
res := n1 + n2
fmt.Println("ok3 res=", res)
return res
}
func main(){
res := sum(10, 20)、
fmt.Println("res的值是", res)
}
以上代码的输出结果为:
ok3 res= 30
ok2 n1= 20
ok1 n1= 10
res的值是 30
函数参数的传递方式
-
值传递:基本的数据类型,int,float,string,数组,结构体,内存一般在栈上分配
-
引用传递:指针,切片,map,管道chan,interface,本质上是地址拷贝,地址拷贝效率更高。内存通常在堆上分配
func test(n1 *int, n2 *int) int{
*n1 = *n2 + 5
return *n1
}
变量作用域
- 函数内部声明或者定义的变量叫局部变量,作用域仅限于函数内部
- 函数外部声明或者定义的变量为全局变量,作用域在整个包都有效,如果首字母大写,这在整个程序都有效
- 在if/for中声明的变量只在这个if/for代码块中有效
- 就近原则:if/for > 函数内部 > 全局变量
常用的字符串函数
// 统计字符串的长度,不用调包,是内建函数
// 返回v中字节的数量,不是元素的数量,是字节的数量,一共汉字是三个字节
func len(v type) int
// 字符串遍历,同时处理有中文的问题
r := []rune(str) //将字符串转为了切片
// 字符串转整数,需要用包strconv, 如果失败会产生erorr
str1 :="12345"
n err := strconv.Atoi(str1)// n==13245
if err != nil{
fmt.Println("转换错误:", err)
}else{
fmt.Println("n的值是:", n)
}
// 整数转为字符串
str2 = strconv.Itoa(123)
fmt.Println("str的值是:", str2)
// 查找一个字符串是否含有某一个子串
strings.Contains(str1, str2)//返回值是bool类型