更新系列
Go语言头秃之路(零)
Go语言头秃之路(一)
Go语言头秃之路(三)
Go语言头秃之路(四)
Go语言头秃之路(五)
Go语言头秃之路(六)
Go语言头秃之路(七)
-
函数与包
函数定义语法:
func 函数名(参数1 参数1类型, 参数2 参数2类型, …) (返回值1类型, 返回值2类型, …) {
语句块
return 返回值1, 返回值2, …
}
当返回值只有一个时,可以不带括号();
当调用获取返回值不需要时使用_占位。
基本数据类型和数组默认都是值传递,即进行值拷贝。在函数内修改,不会影响到原来的值。如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内指针的方式操作变量。package main import "fmt" func func2(n *int) int { *n ++ fmt.Println("func2 n=", *n) return 0 } func main() { var n int = 2 func2(&n) fmt.Println("main n=", n) }- 函数的调用机制:
- 在调用一个函数时,会给改函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其他的栈空间分开
- 当一个函数调用完毕后,程序会销毁这个函数对应的栈空间
- 函数说明
- 基本数据类型和数组默认都是值传递,即进行值拷贝。在函数内修改,不会影响到原来的值。如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内指针的方式操作变量。
- 函数也是一种数据类型,可以作为函数形参(但是要注意形参需要完整的函数定义)
函数是一等公民。 - 自定义数据类型,相当于数据类型的别名:
type 自定义数据类型名 数据类型
自定义数据类型的变量无法赋值相同的基本数据类型的数据,需要进行显式转换 - 可以对函数返回值命名,语法同形参定义(返回值名1 返回值1类型)。
使用返回值命名时,不论返回值有多少个都需要带括号。package main import "fmt" // 返回值命名 func func1(n1 int, n2 int) (count int) { count = n1 + n2 return // 这里不能省 } // 自定义数据类型 type myFun func(int, int) int // 函数作为形参传递 func func2(funVar myFun, num1 int, num2 int) int { return funVar(num1, num2) } func main() { n1 := 2 n2 := 3 a := func1 f1 := a(n1, n2) fmt.Println(f1) f2 := func2(a, n1, n2) fmt.Println(f2) } 可变参数,只能放在形参列表的最后,否则编译不过
语法:变量名 …数据类型
传递参数的时候…func func3(num int, multiVar ...int) (sum int) { sum = num for i:=0;i<len(multiVar);i++ { sum += multiVar[i] } return } ... func main() { r3 := func3(10, -1, 101, 89) fmt.Println(r3) }
练习:传入两个数,值交换
func exchange(a *int, b *int) (int, int) { return *b, *a } func main() { n1 := 1 n2 := 2 aa, bb := exchange(&n1, &n2) fmt.Println(aa, bb) }- init函数:在main函数之前执行,可用于做一些初始化的工作
注意:
程序执行流程:(全局)变量定义 -> init函数 -> mian 函数
如果程序内部有外部变量/函数引用,则先执行外部的(全局)变量定义 和 外部init函数 - 匿名函数
res1 := func (n1, n2 int) int { return n1 + n2 }(10, 20) fmt.Println("res1:", res1) // 全局匿名函数定义方式同下,只是需要首字母大写 var func2 = func (n1, n2 int) int { return n1 - n2 } fmt.Println("res2:", func2(9, 3)) - 闭包:一个函数和其相关应用环境组合的整体
返回一个匿名函数

func Add() func (int) int { // 变量n 和匿名函数组成闭包 var n int = 10 return func (x int) int { n += x return n } } func makeSuffix(suffix string) func (string) string { // 外部传入变量suffix 和匿名函数组成闭包 return func (str string) string { if !strings.HasSuffix(str, suffix) { str += suffix } return str } } func main() { f := Add() fmt.Println(f(1)) fmt.Println(f(2)) fmt.Println(f(3)) f2 := makeSuffix(".jpg") fmt.Println(f2("October.jpg")) } - defer语句
当程序(语句块)遇到defer时,会将defer语句压入栈中(暂时不执行),待程序(语句块)执行结束时,再将栈内语句按照先入后出的顺序(FILO)出栈执行。(即使语句块中有异常仍然会执行defer,类似python中的try…except…finally语句)
defer语句只能是函数调用,不能是表达式(如a++)
当defer将语句压入栈时,也会将相关的值拷贝入栈,当后续代码对相关值的改动不会影响栈中的值。多个defer语句按照入栈的先入后出原则执行。
常用于关闭程序创建的资源,如创建数据库连接、打开文件、锁资源等。

- 参数传递类型
- 值传递:值拷贝,数据量越大则效率越低;包括基本数据类型int/float/bool/string、数组和结构体。
- 引用传递:地址拷贝,效率比较高,因为数据量小;包括指针、slice切片、map、管道、interface等。
值传递变为引用传递只需加上&即可。
- 字符串函数
- len(string)
ASCII字符(字母数字)占一个字节,汉字占3个字节 - []rune(string)
处理带中文的字符串遍历
eg:r := []rune(str) - strconv.Atoi(string)
字符串转整数,返回值有两个:数字和错误
eg: n, err := strconv.Atoi(“123”) - strconv.Itoa(int)
整数转字符串,返回值只有一个 - []byte(string)
字符串转byte
eg:b := []byte(“hello”) - string([]byte{ASCII值})
byte转字符串
eg:str := string([]byte{97, 98, 99}) - strconv.FormatInt(int, 进制)
十进制数转其他进制,返回值是字符串
eg:str := strconv.FormatInt(123, 2) - strings.Contains(string1, string2)
判断字符串是否在另一个字符串中,返回值为bool值
eg:b1 := strings.Contains(“study”, “stu”) - strings.Count(string1, string2)
统计字符串中有几个指定的子串
eg:c := strings.Count(“analysis tools”, “a”) - strings.EqualFold(string1, string2)
不区分大小写字符串比较
eg:b2 := string.EqualFold(“abc”, “ABc”) - strings.Index(string1, string2)
查找子串在字符串中第一次出现的index值(查找最后一次出现的位置用LastIndex),从0开始计
eg:i1 := strings.Index(“asdfghjkl”, “df”) - strings.Replace(原串, 匹配串, 替换串, 替换数)
替换字符串,原串不变,替换数为-1时全部替换 - strings.Split(string, 分隔符)
按照分隔符分割,结果为数组 - strings.ToLower(string)
将所有字符串改为小写,改大写方法为ToUpper - strings.TrimSpace(string)
将字符串两边的空格去掉 - strings.Trim(string, 指定字符)
将字符串两边指定字符去掉,多个指定字符可以写在一起
eg:去掉字符串左右两边的空格和!
strings.Trim("! hello !", " !") - strings.HasPrefix(string, 指定字符串)
判断字符串是否以指定字符串开头,判断是否以指定字符串结尾使用HasSuffix,返回布尔值
bool2 = strings.HasPrefix(“ftp://192.168.9.100”, “ftp”)
- 时间相关
time.Time类型,格式化使用Format(“2006-01-02 15:04:05”)
注意参照格式2006-01-02 15:04:05的数字不能改变,是源码写死的,分隔符可以改动。
时间常量Nanosecond Duration = 1 纳秒
Microsecond = 1000 * Nanosecond 微妙
Millisecond = 1000 * Microsecond 毫秒
Second = 1000 * Millisecond 秒
Minute = 60 * Second 分钟
Hour = 60 * Minute 小时
如休眠100毫秒:time.Sleep(100*time.Millisecond)
注意:时间常量必须与整数运算now := time.Now() fmt.Printf("type: %T \nnow=%v\n", now, now) fmt.Printf(now.Format("2006-01-02")) for i:=1;i<=100;i++ { fmt.Println(i) time.Sleep(time.Millisecond*100) } - 异常处理
在语句块首部加入defer+recover捕获异常:defer func() { err := recover() // 内置函数,用于捕获异常 if err != nil { fmt.Println("err:", err) // 发送邮件告警等 } }()
包注意事项:
- 包中的函数/变量要给外部包使用需要将其命名为
首字母大写,否则将视为私有函数/变量,无法对外使用。 - 文件名和包名一般保持一致
utils.go
package utils
外部包调用时使用包名.的形式进行,如当前在utils文件夹 包名为package abc,那么外部包引入包使用import "…/utils"调用的时候要用abc.Func() - package 指令必须在文件第一行,import紧随其后
import包时,路径从$GOROOT下src开始,不用带src,编译器会自动从src之后开始引入 - 引包时给别名在引包路径之前用空格间隔,取别名后只能用别名调用。
- 在引包路径前用下划线
_表示匿名引用,即使下文没有使用这个包也不会报错; - 在引包路径前用
.相当于把包合并到当前程序,如下栗中使用包方法不用带上包名. 前缀
import . “fmt”
Println(“666”) - 不能出现循环引用;
- 将包编译为可执行文件的条件:
包名需要声明为main,即package main,编译时指定main包所在的文件夹,可以使用-o指定可执行文件位置(注意绝对路径和相对路径)
1786

被折叠的 条评论
为什么被折叠?



