函数声明(定义)
Go 语言函数定义格式如下:
func function_name( parameter list ) return_types {
函数体
}
func
:函数由 func 开始声明
function_name
:函数名称,参数列表和返回值类型构成了函数签名。
parameter list
:参数列表,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types
:返回类型,函数返回一列值。return_types 是该列值的数据类型。函数可以不需要返回值,这种情况下 return_types 不是必须的。
函数体:实现具体功能的代码集合。
实例
这是一个叫add() 函数的代码,该函数传入两个整型参数 num1 和 num2,并返回这两个参数的和
func add(num1 int,num2 int)int{
return num1+num2
}
函数调用
定义了函数之后,可以通过函数名(参数...)
的方式调用函数。 调用上面定义的add()函数
func main(){
a,b:=3,5
ret:=add(3,5)
fmt.Println(ret)
}
函数返回多个值
go语言的函数和其他语言的函数相比的一个特点是go语言的函数可以返回多个值,这些返回值都是要被接收的
看一下下面的例子
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("C++", "golang")
fmt.Println(a, b)
//如果不想接收第二个返回值,可以这样写
a, _ := swap("C++", "golang")
}
参数传递机制
Go 语言的参数传递可以分为按值传递
和按寻引用传递
。 Go 语言默认按值传递,按值传递
传递的是参数的副本,函数接收参数副本后,使用变量的过程中可能对副本的值进行更改,但不会影响原来的变量。换句话说,调用函数时修改参数的值,不会影响原来的实参的值,因为数值变化只作用在副本上。
所以如果要让函数直接修改参数的值,而不是对参数的副本进行修改,就需要将参数的地址(变量名前面添加&符号,比如&var)传递给函数,这就是按“引用传递”。此时传递给函数的是一个指针
如果把指针传递给函数 , 指针的值(地址)就会被复制, 但指针的值指向的地址上的那个值不会被复制(被复制的是指针,但是两个指针指向同一个实际的值) 。 这样一来 , 修改这个指针的值,实际上意味着这个值所指向的地址上的值就被修改了
用文字描述可能有点绕,但是通过下面的例子就比较好理解了
func incr(a int)int{
a++
return a
}
func incrP(a *int)int{
*a++
return *a
}
func main(){
x:=3
fmt.Println("x=",x,"&x=",&x)
y:=incr(x)
fmt.Println("x=",x,"y=",y)
z:=incrP(&x)
fmt.Println("x=",x,"z=",z)
}
incr()函数中 x 变量的值没有发生变化 , 而incrP()函数中 x 的值变了。当调用 incr()的时候, incr()接收的参数其实是 x 的副本,而不是 x 本身;而incrP()函数接收的参数是一个指针,指向的是 x 的本身,所以 x实际的值就改变了 。
函数作为类型
type addFunc func(a,b int)int
这段代码的意思是,长这样的函数func(a,b int)int属于addFunc类型,既然有了类型,那么就可以定义这样类型的变量。
type addNum func(int,int)int
func main(){
var a addNum=add
b:=add
fmt.Println(a(1,3))
fmt.Println(b(5,7))
}
func add(a,b int)int{
return a+b
}
go语言中函数也是值(first-class value)可以赋值给变量
但是,如果现在还有一个函数f。
func f(s string,x,y int) string{
fmt.Sprintf(s,"%d %d",x,y)
return s
}
那么,显然f不能赋值给a(类型不一样),f也不能赋值给b,因为b的类型也确定了,和f不一样。
函数作为参数
既然函数可以作为类型,那么我们就可以把这个类型的函数当成值来进行传递。
type FormatFunc func(s string,x,y int) string
func main(){
b:=format(func(s string,x,y int)string{
return fmt.Sprintf(s,x,y)
},"%d%d",1,2)
fmt.Println(b)
}
func format(ff FormatFunc,s string,x,y int) string{
return ff(s,x,y)
}
可变参数
如果一个函数的最后一个参数是…type的形式那么这里函数可以处理类型为type的变长参数。
func minAndmax(s string ,a...int)int{
if len(a)==0{
return -1
}
if s=="max"{
max:=a[0]
for _,v:=range a{
if v>max{
max=v
}
}
return max
}else if s=="min"{
min:=a[0]
for _,v:=range a{
if v<min{
min=v
}
}
return min
}else{
return -1
}
}
传递参数的方法有两种,一种是手动填写参数,一种是将这堆数据存在数组arr中,通过arr…传递。
//手动填写参数
age:=minAndmax("min",9,3,1,5,2)
fmt.Printf("最小的年龄为%d\n",age)
//数组作为参数
arr:=[]int{2,4,1,5,6,3}
age=minAndmax("max",arr...)
fmt.Printf("最大的年龄为%d\n",age)
匿名函数/闭包
匿名函数是在函数声明时没有名字的一种函数,不能独立存在但是可以被赋值给某一个变量
func(num int){
sum:=0
for i:=0;i<=num;i++{
sum+=i
}
fmt.Println(sum)
}
声明之后需要立即被调用或赋值给一个变量,不然的话就会报错。
闭包函数赋值
a:=func(num int){
sum:=0
for i:=0;i<=num;i++{
sum+=i
}
fmt.Println(sum)
}
闭包函数调用
在函数体后面加上括号,如果有参数就传入参数。
func(num int){
sum:=0
for i:=0;i<=num;i++{
sum+=i
}
fmt.Println(sum)
}(100)
闭包函数继承了声明时的作用域
func main(){
j:=5
a:=func() func(){
i:= 10
return func() {
fmt.Printf("i=%d j=%d\n",i,j)
}
}()
a()
}
由闭包函数继承了声明时的作用域可知,这个闭包访问在声明前的变量j是合法的。
闭包函数可以保存并累积变量值
func main(){
func1:=ins()
fmt.Println(func1())
fmt.Println(func1())
fmt.Println(func1())
func2:=ins()
fmt.Println(func2())
fmt.Println(func2())
}
func ins() func() int{
i:=0
return func() int{
i++
return i
}
}