设为首页 加入收藏

TOP

Go基础系列:函数(1)(一)
2018-10-30 14:08:45 】 浏览:425
Tags:基础 系列 函数

Go中函数特性简介

对Go中的函数特性做一个总结。懂则看,不懂则算。

  1. Go中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。
  2. Go编译时不在乎函数的定义位置,但建议init()定义在最前面(如果有的话),main函数定义在init()之后,然后再根据函数名的字母顺序或者根据调用顺序放置各函数的位置。
  3. 函数的参数、返回值以及它们的类型,结合起来成为函数的签名(signature)。
  4. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
    • 由于引用类型(slice、map、interface、channel)自身就是指针,所以这些类型的值拷贝给函数参数,函数内部的参数仍然指向它们的底层数据结构。
  5. 函数参数可以没有名称,例如func myfunc(int,int)
  6. Go中的函数可以作为一种type类型,例如type myfunc func(int,int) int
    • 实际上,在Go中,函数本身就是一种类型,它的signature就是所谓的type,例如func(int,int) int。所以,当函数ab()赋值给一个变量ref_abref_ab := ab,不能再将其它函数类型的函数cd()赋值给变量ref_ab
  7. Go中作用域是词法作用域,意味着函数的定义位置决定了它能看见的变量。
  8. Go中不允许函数重载(overload),也就是说不允许函数同名。
  9. Go中的函数不能嵌套函数,但可以嵌套匿名函数。
  10. Go实现了一级函数(first-class functions),Go中的函数是高阶函数(high-order functions)。这意味着:
    • 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数
    • 函数可以作为参数传递给另一个函数
    • 函数的返回值可以是一个函数
    • 这些特性使得函数变得无比的灵活,例如回调函数、闭包等等功能都依赖于这些特性。
  11. Go中的函数不支持泛型(目前不支持),但如果需要泛型的情况,大多数时候都可以通过接口、type switch、reflection的方式来解决。但使用这些技术使得代码变得更复杂,性能更低。

参数和返回值

函数可以有0或多个参数,0或多个返回值,参数和返回值都需要指定数据类型,返回值通过return关键字来指定。

return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。Go中的函数可以有多个返回值。

  • (1).当返回值有多个时,这些返回值必须使用括号包围,逗号分隔
  • (2).return关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称
  • (3).当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
  • (4).但即使返回值命名了,return中也可以强制指定其它返回值的名称,也就是说return的优先级更高
  • (5).命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量重复定义
  • (6).return中可以有表达式,但不能出现赋值表达式,这和其它语言可能有所不同。例如return a+b是正确的,但return c=a+b是错误的

例如:

// 单个返回值
func func_a() int{
    return a
}

// 只要命名了返回值,必须括号包围
func func_b() (a int){
    // 变量a int已存在,无需再次声明
    a = 10
    return
    // 等价于:return a
}

// 多个返回值,且在return中指定返回的内容
func func_c() (int,int){
    return a,b
}

// 多个返回值
func func_d() (a,b int){
    return
    // 等价于:return a,b
}

// return覆盖命名返回值
func func_e() (a,b int){
    return x,y
}

Go中经常会使用其中一个返回值作为函数是否执行成功、是否有错误信息的判断条件。例如return value,existsreturn value,okreturn value,err等。

当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如,同类型的返回值可以放进slice中,不同类型的返回值可以放进map中。

但函数有多个返回值时,如果其中某个或某几个返回值不想使用,可以通过下划线_这个blank identifier来丢弃这些返回值。例如下面的func_a函数两个返回值,调用该函数时,丢弃了第二个返回值b,只保留了第一个返回值a赋值给了变量a

func func_a() (a,b int){
    return
}

func main() {
    a,_ := func_a()
}

按值传参

Go中是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问、修改的也是这个副本。

例如:

a,b := 10,20
min(a,b)
func min(x,y int) int{}

上面调用min()时,是将a和b的值拷贝一份,然后将拷贝的副本赋值给变量x,y的,所以min()函数内部,访问、修改的一直是a、b的副本,和原始的数据对象a、b没有任何关系。

如果想要修改外部数据(即上面的a、b),需要传递指针。

例如,下面两个函数,func_value()是传值函数,func_ptr()是传指针函数,它们都修改同一个变量的值。

package main

import "fmt"

func main() {
    a := 10
    func_value(a)
    fmt.Println(a)    // 输出的值仍然是10
    
    b := &a
    func_ptr(b)
    fmt.Println(*b)   // 输出修改后的值:11
}

func func_value(x int) int{
    x = x + 1
    return x
}

func func_ptr(x *int) int{
    *x = *x + 1
    return *x
}

map、slice、interface、channel这些数据类型本身就是指针类型的,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。

另外注意,赋值操作b = a+1这种类型的赋值也是拷贝赋值。换句话说,现在底层已经有两个数据对象,一个是a,一个是b。但a = a+1这种类型的赋值虽然本质上是拷贝赋值,但因为a的指针指向特性,使得结果上看是原地修改数据对象而非生成新数据对象。

变长参数"..."(variadic)

有时候参数过多,或者想要让函数处理任意多个的参数,可以在函数定义语句的参数部分使用ARGS...TYPE的方式。这时会将...代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE。

...在Go中称为variadic,在使用...的时候(如传递、赋值),可以将它看作是一个slice,下面的几个例子可以说明它的用法。

例如:func myfunc(a,b int,args...int) int {}。除了前两个参数a和b外,其它的参数全都保存到

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇some settings for spacemacs gol.. 下一篇Go基础系列:流程控制结构

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目