如果你遇到没有函数体的函数声明,表示该函数不是以Go实现的。

  1. package math
  2.  
  3. func Sin(x float64) float //implemented in assembly language

  1. 如果为函数的每一个返回值都设置变量名,则会以相应的零值初始化,且在该函数的return语句中省略操作数,这种用法称之为 bare return
  2. go中的错误处理,习惯上是先进行一系列的初始化检查,将处理失败逻辑的代码先行处理,然后才是函数的实际逻辑,这样使得代码更简洁,避免过多的层级结构。
  3. 函数定义时,可以使用函数类型作为参数,也可以作为返回类型,是不是有点类似委托,从而实现闭包。此外还有匿名函数,是不是类似于lambda表达式。strings.Map 函数可以拿来试验。
  1. func squares() func() int {
  2. var x int
  3. return func() int {
  4. x++
  5. return x * x
  6. }
  7. }
  8. func main() {
  9. f := squares()
  10. fmt.Println(f()) // "1"
  11. fmt.Println(f()) // "4"
  12. fmt.Println(f()) // "9"
  13. fmt.Println(f()) // "16"
  14. }
  1.  
  1.  

匿名函数和squares中,存在变量引用。这就是函数值属于引用类型和函数值不可比较的原因。Go使用闭包(closures)技术实现函数值,Go程序员也把函数值叫做闭包。

注意golang圣经中匿名函数一节中的例子程序。

go语言的可变参函数非常好用,你可以传递多个同类型参数,也可以直接传入一个该类型的切片(注意传入切片时要使用...标记,我想应该是为了同切片参数区分吧,毕竟两者还是有些不同的),如果想要使用不同类型的变参,那么使用万能的 interfac{} ,函数体内像解析切片一样解析这个变参就好了。

直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反

  1. var mu sync.Mutex
  2. var m = make(map[string]int)
  3. func lookup(key string) int {
  4. mu.Lock()
  5. defer mu.Unlock()
  6. return m[key]
  7. }
  8.  
  9. 调试复杂程序时,defer机制也常被用于记录何时进入和退出函数。
  1. func bigSlowOperation() {
  2. defer trace("bigSlowOperation")() // don't forget the
  3. extra parentheses
  4. // ...lots of work…
  5. time.Sleep(10 * time.Second) // simulate slow
  6. operation by sleeping
  7. }
  8. func trace(msg string) func() {
  9. start := time.Now()
  10. log.Printf("enter %s", msg)
  11. return func() {
  12. log.Printf("exit %s (%s)", msg,time.Since(start))
  13. }
  14. }
  15.  

我们只需要首先命名double的返回值,再增加defer语句,我们就可以在double每次被调用时,输出参数以及返回值。

  1. func double(x int) (result int) {
  2. defer func() { fmt.Printf("double(%d) = %d\n", x,result) }()
  3. return x + x
  4. }
  5. _ = double(4)
  6. // Output:
  7. // "double(4) = 8"

 

太利于调试了。。。defer语句还是要细看。

为了方便诊断问题,runtime包允许程序员输出堆栈信息。在下面的例子中,我们通过在main函数中延迟调用printStack输出堆栈信息。

  1. gopl.io/ch5/defer2
  2. func main() {
  3. defer printStack()
  4. f(3)
  5. }
  6. func printStack() {
  7. var buf [4096]byte
  8. n := runtime.Stack(buf[:], false)
  9. os.Stdout.Write(buf[:n])
  10. }
  11.  
  12. 不能为一个结构体定义同名的字段名和方法名,有点奇怪。
  1.  
  2. 函数指针:go里其实也是有函数指针的,下面用go语言实现表驱动模式。
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. )
  6.  
  7. func add(a int, b int) int {
  8. return a + b
  9. }
  10.  
  11. func sub(a int, b int) int {
  12. return a - b
  13. }
  14.  
  15. func main() {
  16. fm := make(map[int]func(int, int) int)
  17. fm[] = add
  18. fm[] = sub
  19. protocol :=
  20. i :=
  21. j :=
  22. if func_handle, ok := fm[protocol]; ok {
  23. println(func_handle(i, j))
  24. } else {
  25. fmt.Printf("protocol: %d not register!", protocol)
  26. }
  27. }
  1.  

返回局部变量指针:

  不同于 C 语言,GO 的函数可以返回局部变化指针,且编译器会通过逃逸分析(escape analysis)来决定是否在堆上分配内存。

编译时可以通过 -gcflags "-l -m" 参数来禁用函数内联,函数内联会对内存分配有一些影响,具体不清楚。

函数参数没有所谓的引用传递,都是值传递的,区别只是传递的是拷贝对象还是指针而已。在 C 语言中,一般推荐传递指针参数来避免复制对象提升效率,但在 go 中,被复制的指针会延长目标对象的生命周期,还可能导致它被分配到堆上,则性能消耗要加上堆内存分配和垃圾回收的成本,而在栈上复制小对象其实非常快,所以如果不是特别大的对象或确实需要修改原对象,一般不需要传指针参数。在并发编程中,也提倡使用不可变对象(只读或复制),可以消除数据同步的麻烦。

如下就会在堆上分配内存,编译时通过 -gcflags "-m" 可查看汇编代码:

  1. func test(p *int) {
  2. go func() {
  3. println(p)
  4. }()
  5. }
  6.  
  7. func main() {
  8. x :=
  9. p := &x
  10. test(p)
  11. }

使用传出参数,推荐使用返回值,也可以使用二级指针:

  1. func test(p **int) {
  2. x :=
  3. *p = &x
  4. }
  5.  
  6. func main() {
  7. var p *int
  8. test(&p)
  9. println(*p)
  10. }
  1.  

golang笔记——函数与方法的更多相关文章

  1. golang学习笔记---函数、方法和接口

    函数:对应操作序列,是程序的基本组成元素. 函数有具名和匿名之分:具名函数一般对应于包级的函数,是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心 ...

  2. golang学习笔记--函数和方法

    在go中,函数类型是一等类型,这意味着可以吧函数当做一个值来传递和使用. func divide(dividend int,divisor int)(int,error){ //省略部分代码 } 参数 ...

  3. golang 函数和方法

    由于自己是搞python开发的,所以在学习go时,当看到函数和方法时,顿时还是挺蒙的,因为在python中并没有明显的区别,但是在go中却是两个完全不同的东西.在官方的解释中,方法是包含了接收者的函数 ...

  4. opencv-学习笔记(1)常用函数和方法。

    opencv-学习笔记(1)常用函数和方法. cv2.imread(filename,falg) filename是文件名字 flag是读入的方式 cv2.MREAD_UNCHANGED :不进行转化 ...

  5. Go语言学习笔记(7)——函数和方法

    Go语言中同时有函数和方法! 函数: go程序必须要包含一个main函数.main函数不能有任何参数和返回值! 1. 定义方法示例: func max(num1, num2 int) int { // ...

  6. Golang 函数以及函数和方法的区别

    在接触到go之前,我认为函数和方法只是同一个东西的两个名字而已(在我熟悉的c/c++,python,java中没有明显的区别),但是在golang中者完全是两个不同的东西.官方的解释是,方法是包含了接 ...

  7. 学习笔记:jquery1.9版本后废弃的函数和方法

    jQuery1.9+ 废弃的函数和方法 升级Jquery版本遇到的问题 (转载自:http://www.ppblog.cn/jquery1-9live.html  版权归原作者所有) jQuery1. ...

  8. GOLANG 匿名函数笔记

    在函数内部,没有名字的函数,就是匿名函数 实现方法1: func main(){ a := "我是无参无返回值的匿名函数" x := func(){ //可以获取到匿名函数外部的变 ...

  9. Golang笔记(二)面向对象的设计

    Golang笔记(二)面向对象的设计 Golang本质还是面向过程的语言,但它实现了一些OOP的特性,包括抽象.封装.继承和多态. 抽象和封装 Golang和C语言一样以struct为数据结构核心,不 ...

随机推荐

  1. 烂泥:openvpn tun模式下客户端与内网机器通信

    本文由秀依林枫提供友情赞助,首发于烂泥行天下 前两篇文章我们介绍了有关openvpn的搭建与配置文件的讲解,这篇文章我们再聊介绍下,在tun模式下openvpn客户端如何与内网机器通信的问题. 一.实 ...

  2. Oracle安装注意点与工具使用简说

    oracle数据库安装 注意点:orcl,安装过程中指定sys,system等相关账户密码 scott账户下有常用的四张表,可用system或sys作为sysdba进去, 可alter user sc ...

  3. Android APP 读取 AndroidManifest.xml 中的版本信息详解

    APP都会涉及到版本的问题,Android APP的版本信息保存在AndroidManifest.xml文件的顶部.如下图: 有2个属性表示,“android:versionCode”和“androi ...

  4. NOIP2001 一元三次方程求解[导数+牛顿迭代法]

    题目描述 有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程.给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差 ...

  5. video标签播放视频

    <!doctype html> <html> <head> <meta charset=utf-8> <title>测试</title ...

  6. svg拉伸,原来凹凸可以这么玩

    原文:http://www.smartjava.org/content/render-geographic-information-3d-threejs-and-d3js The last coupl ...

  7. Java中的instanceof关键字

    instanceof是Java的一个二元操作符,和==,>,<是同一类东东.由于它是由字母组成的,所以也是Java的保留关键字.它的作用是测试它左边的对象是否是它右边的类的实例,返回boo ...

  8. C/C++实践笔记 005

    整型常量int a=101u; 无符号整数int b=102l; 长整数int c=103ll; long long整数 存储qq号,手机号 010八进制 0x10十六进制 嵌入式的场合经常用shor ...

  9. win7/8 访问 访问局域网 默认加载域 而无法成功访问的问题

    运行 gpedit.msc选择 本地计算机策略- 计算机配置-windows设置-安全设置-本地策略-安全选项 其中有个 网络安全:lan管理员身份验证级别 选择 发送LM和NTLM响应

  10. WCF开发那些需要注意的坑 Z

    执行如下 批处理:"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe" http://127.0.0.1: ...