学习golang(7) 初探:函数(1)
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情。
虽然编程语言都大相径庭,但是每个语言都还是有一些特性的,所以我们还是要保持空杯心态去看待这件事情,不焦虑,不浮躁,如果觉得自己学到了某些,那么可以将它整理成文档,这也许就是掘金更文的初衷吧。
为什么需要函数
我们可以将一个非常复杂的工作,分为多个子任务,分而治之,不仅如此,假如我们编写一个程序,该程序有n条语句,被多次使用,那么我们有2种调用方法,1. 直接在程序中使用相同的代码 2. 创建一个函数,并在主函数中执行函数即可。还有一个点是,我们使用函数,让多人协作变得更加容易。
为什么我们需要函数?
- 让多人协作变得很容易
- 减少没必要的重复代码
- 提高代码可读性
举个例子:
例如我们在程序中经常输出一些语句fmt.Println("123")
这里其实就是函数,只不过它为系统函数,我们为此,不需要重新打印语句,而直接调用即可。
再举个例子:
例如我们想写一个程序,该程序的主要功能为:把大象放进冰箱,那么我们将此划分为三个函数
- 函数1: 根据大象的体型,找到能够装这么大的并且,并且返回 地址和冰箱门
- 函数2: 调用函数1,把大象运至该地址
- 函数3:将大象放入冰箱,并且关闭冰箱门
为此,我们就可以将这个工程,分为三个人,或者三个不同部分协作处理了,每人/每个团队只用根据文档,返回相应的值即可。
函数的调用逻辑
函数调用逻辑概述
函数的调用过程逻辑非常复杂,涉及到了很多底层的东西,我看了一阵子,没怎么学过汇编,我也没怎么看懂,我们来看调用栈的过程吧,
假设 主函数中 调用了函数A,函数A调用了 函数B ,函数B又调用了函数C,那么其调用过程应该为
最先被调用的函数(例如: main) 最后被执行完毕
最后被调用的函数(C)最先被执行完毕
尝试使用调试工具分析调用逻辑
我们虽然暂时还看不懂,汇编的东西,但是我们可以debug
一下函数调用栈
操作步骤
layout src
显示源代码窗口l
查看代码- 使用
b
设置断点 - 使用
run
运行代码 - 使用
c
切换到端点 - 使用
bt
查看调用栈
如图,我们能够清楚的看到函数调用栈:
main
函数最先入栈,最后一个出栈,而c
函数最后入栈,却最先出栈。
函数的基本使用
函数格式
go
函数格式如下
func 函数名称(函数参数列表) (返回值列表) {
...
}
例如我们写一个返回程序,该程序有一个函数add1
,add1
接收一个int
的参数,并且返回该参数-1的值
函数定义如下
func add1(x int)(int) {
return x-1
}
我们执行该程序
返回多个值
我们可以使用go
返回多个值,还记得之前我们写的tcp
服务器 么? 其中就返回了多个值
语法如下
func 函数名称(参数)(返回类型A,返回类型B) {
}
我们来尝试下
我们改写下程序,将原参数-1 和 原参数 的值返回回去
我们执行一下语句
不仅如此,我们还可以隐式返回,我们修改下代码
我们在函数返回上定义变量,然后赋值起变量,最后使用return
,此语句也会返回2
个参数,不过一般用的不是很多
我们执行下
以上就是函数的基本使用,简单吧,是不是和其他语言差不多。
go函数的特性
除了和别的语言差不多的函数声明,go
还提供了其他的函数申明,来看下
匿名函数
我们再func
后没有添加函数名称,这个称之为匿名函数,我们可以将上述例子修改为匿名函数的形式
我们执行下代码
什么时候会用到匿名函数呢?
我们若仅在某个时刻使用该函数一次,后面我们就不用了,我们则可以使用匿名函数,比如,我们利用协程处理事情
我们执行下
defer代码
我们进程在go
中看到defer
, 其实它的作用为: 在函数结束完毕后,执行defer
语句(不管函数是否正常都会执行defer
)
例如,我们有如下代码
从上述我们可以知道,defer
是在return
结束完毕后再执行的,具体的我们执行下代码看看
还有一点,若我们在写defer
之前,程序就崩溃了,此时是不会调用defer
语句的,例如
我们执行下代码
总结
go
因为没有try
,所以,我们判断是否正常,就很需要函数返回正常的值,和error
,我们再判断error
是否为nil
,若为nil
则代表函数执行成功,否则执行出错。
此外,defer
真的很强大,在别的语言中,我们一般打开一个文件,都会在最后关掉句柄,虽然go
有回收机制,但是回收机制,一般用于在程序中申请的变量,不包括打开的系统资源,所以,我们可以在打开后,设置defer
关闭文件,这样绝不会出错,例如
fs , err := os.Open("123.txt")
if err != nil {
return
}
defer fs.Close()
今天就这样吧,今天学gdb
花了太多时间了。,以前没怎么用gdb
调试过,我一般调试代码还没有达到栈级别,都是使用打印大法print
调试的。
溜了 溜了