目录

文章目录

  • 目录
  • 定义一个函数
    • 形参列表
      • 值传递
      • 引用传递
    • 返回值
  • 初始化函数
  • 构造函数
  • 析构函数
  • 回调函数
  • 闭包(Closure)函数
  • 方法函数
  • 递归函数

定义一个函数

函数声明需要指定:

  • 函数的名称
  • 形参列表
  • 返回值列表

函数名和形参列表一起构成函数签名。格式:

func function_name([parameter list]) [return_types] {函数体
}

示例:

func max(num1, num2 int) int {var result intif (num1 > num2) {result = num1} else {result = num2}return result
}

形参列表

值传递

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。默认情况下,Golang 使用的是值传递,即在调用过程中不会影响到实际参数。

示例:

package mainimport "fmt"func swap(x, y int) int {var temp inttemp = xx = yy = tempreturn temp;
}func main() {var a int = 100var b int = 200fmt.Printf("交换前 a 的值为 : %d\n", a)fmt.Printf("交换前 b 的值为 : %d\n", b)swap(a, b)fmt.Printf("交换后 a 的值 : %d\n", a)fmt.Printf("交换后 b 的值 : %d\n", b)
}

结果:可见,值传递的方式不会影响到实参变量的原数值

交换前 a 的值为 : 100
交换前 b 的值为 : 200
交换后 a 的值 : 100
交换后 b 的值 : 200

引用传递

引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。类似于 C 语言函数定义中的指针类型形参。

引用传递方式,将指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:

package mainimport "fmt"func swap(x, y *int) {*x, *y = *y, *x
}func main() {var a int = 100var b int= 200fmt.Printf("交换前,a 的值 : %d\n", a)fmt.Printf("交换前,b 的值 : %d\n", b)swap(&a, &b)fmt.Printf("交换后,a 的值 : %d\n", a)fmt.Printf("交换后,b 的值 : %d\n", b)
}

结果:

交换前,a 的值 : 100
交换前,b 的值 : 200
交换后,a 的值 : 200
交换后,b 的值 : 100

返回值

Go 函数可以返回多个值,例如:

package mainimport "fmt"func swap(x, y string) (string, string) {return y, x
}func main() {a, b := swap("hello", "world")fmt.Println(a, b)
}

结果:

world hello

初始化函数

Golang 程序的初始化流程:

  1. 初始化导入的包,包的初始化顺序由 runtime 需要解析包依赖关系决定,没有依赖的包最先被初始化。
  2. 初始化包作用域的变量,该作用域的变量初始化也并非按照 “从上到下、从左到右” 的顺序,同样由 runtime 解析变量依赖关系,没有依赖的变量最先初始化。
  3. 执行包的 init 函数;

Golang 提供了一个特殊的初始化函数 init(),会先于 main() 函数执行,通常用于实现包级别的一些初始化操作。例如:在 ORM database 包中的 init() 通常用于实现 AutoMigrate(数据库初始化)。

init() 的主要特点:

  • init 函数先于 main 函数执行,且不能被其他函数调用;
  • init 函数没有输入参数,也没有返回值;
  • 每个包可以有多个 init 函数;
  • 包的每个源文件也可以有多个 init 函数,这点比较特殊;
  • Golang 没有明确定义同一个包中多个 init 函数的执行顺序,编程时要注意程序不要依赖 init 函数的执行顺序。
  • 不同包之间的 init 函数按照包导入的依赖关系决定执行顺序。

构造函数

用于构造一个实例对象的函数,build-in 的构造函数有 new() 用于构造自建数据类型的实例。如果是自定义派生数据类型的构造函数通常是开发者自己实现的。

析构函数

Golang 原生没有析构函数,需要开发者额外的人工处理资源清理的工作,通常会使用 defer 语句来完成这些工作。

回调函数

回调函数,一个函数作为另外一个函数的实参,即:向函数传递一个函数的引用。显然,这是一个引用传递的场景。这与 C 语言中的函数指针(指向函数的指针)类似。

所以,在传递一个函数之前,首先需要创建一个函数变量,函数变量的名称就是函数自身的一个引用。

package mainimport ("fmt""math"
)func get_square_root(x float64) float64 {return math.Sqrt(x)
}func main() {fmt.Println(get_square_root(9))
}

结果:

3

或者,可以直接在一个函数体中定义一个新的函数:

package mainimport ("fmt""math"
)func main() {/* 定义函数变量。 */get_square_root := func(x float64) float64 {return math.Sqrt(x)}fmt.Println(get_square_root(9))
}

实际上,当我们定义一个接受函数实参的函数时,首先需要声明函数形参的类型:

package mainimport "fmt"func test_call_back(x int, f func(int) int) {f(x)
}func call_back(x int) int {fmt.Printf("我是回调,x:%d\n", x)return x
}func main() {test_call_back(1, call_back)
}

更好的一种写法:

package mainimport "fmt"type call_back_func_t func(int) intfunc test_call_back(x int, f call_back_func_t) {f(x)
}func call_back(x int) int {fmt.Printf("我是回调,x:%d\n", x)return x
}func main() {test_call_back(1, call_back)
}

再看一种写法:

package mainimport "fmt"type call_back_func_t func(int) intfunc test_call_back(x int, f call_back_func_t) {f(x)
}func main() {test_call_back(1, func(x int) int {fmt.Printf("我是回调,x:%d\n", x)return x})
}

闭包(Closure)函数

闭包(Closure):如果内层函数引用了外层函数的局部变量,并且在外层函数中 return 内层函数,这种关系就称之为闭包。

可见,闭包的特点是发生在函数嵌套的基础上实现。外层函数返回的内层函数还引用了外层函数的局部变量,所以要想正确的使用闭包,那么就要确保这个被内层函数引用的局部变量是不变的。Python 中使用闭包函数来实现了装饰器机制。

Golang 支持匿名函数,可用于实现闭包。匿名函数是一个表达式吗,其优越性在于可以直接使用函数内的变量,而不必声明函数名。以下示例中,定义了函数 getSequence() 并返回了另外一个函数。getSequence() 函数的目的是在闭包中递增 i 变量。

package mainimport "fmt"/*** get_sequence 函数作为外层函数,返回匿名函数 func() int,* 匿名函数 func() int 作为内层函数,并且直接引用了外层函数的局部变量 i,* 如此的,就形成了一个闭包。*/
func get_sequence() func() int {i := 0return func() int {i += 1return i  }
}func main() {/* 定义一个闭包函数变量,当前 i 变量为 0。 */next_num := get_sequence()  /* 每调用一次闭包函数,变量 i += 1 并返回。 */fmt.Println(next_num())fmt.Println(next_num())fmt.Println(next_num())/* 定义另一个闭包函数变量。 */next_num1 := get_sequence()  fmt.Println(next_num1())fmt.Println(next_num1())fmt.Println(next_num1())
}

结果:

1
2
3
1
2
3

方法函数

在常规的面向对象编程语言(OOP)中,比如 Python,函数(Function)和方法(Method)是属于两个不同的术语:

  • 在类中定义的称为方法,称为类对象的成员方法。
  • 不在类中定义的称为独立函数。

虽然 Golang 不是一种 OOP 类型编程语言,没有类的概念,但它也同样支持为数据类型定义相应的 Method。所谓的 Method 其实就是函数,只不过与普通函数相比,这类函数是作用在某个数据类型上的。

所以,在函数签名中,会有个 Receiver(接收器)来表明当前定义的函数会作用在该 Receiver 上。一个 Method 就是一个包含了 Receiver 的函数,接受者可以是:“命名(type)类型” 或者 “结构体类型” 的一个值或一个指针。例如下述的 (variable_name variable_data_type)

func (variable_name variable_data_type) function_name() [return_type]{/* 函数体*/
}

实际上,Golang 支持对除 Interface 类型外的任何数据类型定义其 Method,只不过实际编程中,Method 多定义在结构体上而已。

从这点上,我们可以简易的将结构体类型变量理解为 Python 中对象的概念,而对象可以调用属于他自身的方法。这是 Golang 对 OOP 编程机制的一种补充。

示例:

package mainimport "fmt"/* 自定义结构体类型 */
type circle_t struct {radius float64
}// 定义属于 circle 类型对象的 Method
func (c circle_t) get_area() float64 {// c.radius 即为 circle 类型对象中的属性return 3.14 * c.radius * c.radius
}func main() {// 定义一个结构体类型对象 c1var c1 circle_tc1.radius = 10.00fmt.Println("圆的面积 = ", c1.get_area())
}

从上述例子可见,方法 get_area 就像是变量 c1 的成员方法一般。

递归函数

递归,就是在运行的过程中调用自己。Golang 支持递归调用,但我们在使用递归时,需要合理设置退出条件,否则递归将陷入无限循环中。

格式:

func recursion() {recursion() /* 函数调用自身 */
}func main() {recursion()
}

递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。

  • 阶乘:
package mainimport "fmt"func Factorial(n uint64) (result uint64) {if (n > 0) {result = n * Factorial(n-1)return result}return 1
}func main() {  var i int = 15fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}
  • 斐波那契数列:
package mainimport "fmt"func fibonacci(n int) int {if n < 2 {return n}return fibonacci(n-2) + fibonacci(n-1)
}func main() {var i intfor i = 0; i < 10; i++ {fmt.Printf("%d\t", fibonacci(i))}
}

Go 语言编程 — 函数相关推荐

  1. C 语言编程 — 函数

    目录 文章目录 目录 前文列表 函数 函数的声明 函数的定义 函数的形参与实参 值传递 引用传递 可变长形参列表 函数的调用 函数的指针 回调函数 递归函数 数的阶乘 斐波那契数列 构造函数(Cons ...

  2. arg是什么函数_C 语言编程 — 函数

    函数 函数的本质就是针对变量的操作过程,同时可能也会改变当前程序的状态.它接受多个输入值,计算并返回一个输出值. 函数大体上分为 3 类: 主函数:每个 C 程序都至少有一个 main(). 内置函数 ...

  3. 51单片机c语言编程函数,单片机C语言教程:C51函数

    其实一直出现在例子中的 main()也算是一个函数,只不过它比较特殊,编译时以它做为程序的开始段.有了函数C 语言就有了模块化的优点,一般功能较多的程序,会在编写程序时把每项单独的功能分成数个子程序模 ...

  4. 的c语言编程函数题,C语言编程题及答案Word版

    <C语言编程题及答案Word版>由会员分享,可在线阅读,更多相关<C语言编程题及答案Word版(122页珍藏版)>请在人人文库网上搜索. 1.传播优秀Word版文档 ,希望对您 ...

  5. c语言 编程 函数声明,C语言编程开发中的函数声明与定义

    函数功能的使用在许多编程开发语言中都是有不同的使用方法的,而今天我们就一起来了解一下,在C语言编程开发中的函数功能使用与定义. 对函数的"定义"和"声明"不是一 ...

  6. c语言编程函数补充上机题,2011年计算机二级C语言上机操作题及答案(10)

    /********found********/ void fun(char  *s, *t1, *t2, *w) { int  i; char  *p, *r, *a; strcpy(w, s); w ...

  7. python语言编程函数_Python编程语言的函数功能

    编程语言导游财报抄写员. 工具/原料 电脑 Python 工作原理 1 如果你有一定的Python基础,想必现在已经熟悉print(),input(),len()函数.Python提供这样一些内建函数 ...

  8. 用C语言编程函数求圆台体积,第六篇 AutoCAD二次开发技术.pdf

    第六章 ObjectARX 开发技术 1. AutoCAD二次开发概述 7. MFC对话框 2. ObjectARX简介 8. 搜索图形数据库及获取实体 3. 常用实体类和几何类 数据 4. 创建实体 ...

  9. c语言编程函数名:b开头

    函数名:bar 功 能:画一个二维条形图 用 法:void far bar(int left,int top,int right,int bottom); 程序例: #include <grap ...

最新文章

  1. 飓风“桑迪”路径图的制作
  2. 动态滴定预测算法研究
  3. 【Android 内存优化】Bitmap 图像尺寸缩小 ( 考虑像素密度、针对从不同像素密度资源中解码对应的 Bitmap 对象 | inDensity | inTargetDensity )
  4. Linux缺少qt5core,关于qt5:无法运行Qt应用:找不到版本“ Qt_5”
  5. Java自动拆箱陷阱。 谨防!
  6. 使用 SQL 语句实现一个年会抽奖程序
  7. Nginx模拟负载均衡
  8. Python_将conda里的环境导入到pychram中
  9. Qt_QSS 样式表属性大全
  10. 周跃计算机网络安全基础,华图网校金领名师模块班:计算机基础知识
  11. 可自主二次开发的微信云控客服crm系统软件(带源码)
  12. 计算机存储一个像素点需要多少个字节
  13. 《SuperMap iDesktop的理论知识》
  14. 2009中国情爱报告:超4成未婚女性担心嫁不出去
  15. 同样是学编程,为什么别人十几岁就成了黑客,而你还在做码农
  16. java sort 降序_详解Java sort()数组排序(升序和降序)
  17. 以太坊测试网络rinkeby交易测试
  18. vue-cli3 接口api代理 308 Permanent Redirect
  19. ikun(小黑子)必须知道的坤坤谐音梗
  20. Eclipse Java文件注释\签名模板

热门文章

  1. Xamarin.Forms开发实战基础篇大学霸内部资料
  2. 计算机电缆怎么做耐压试验,ZGF直流高压发生器进行电缆耐压试验的6个步骤
  3. 文件打开不关闭程序会输出吗_干货!很多资深用户都不知道的10个BOOX阅读器使用技巧,很实用!...
  4. c++ 回调函数_Java中的回调机制,这篇给你整的明明白白的
  5. 机器学习算法_五分钟了解机器学习十大算法
  6. 用于视力恢复的脑机接口综述(一)(修改)
  7. Unity 总裁 John Riccitiello 发话:VR足以匹敌互联网
  8. java 数据合并算法_Java与算法之(11) - 合并排序
  9. id 怎么获取jira 评论_一篇文章教会你使用Python定时抓取微博评论
  10. 2021未来科学大奖揭晓:SARS病原发现者、上海交大张杰教授等4人获得百万奖金...