如果你遇到没有函数体的函数声明,表示该函数不是以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. logback配置详解4-实例配置

    莫个银行项目中实际引用的logback实例,提供大家参考!!!! [html] view plaincopy <?xml version="1.0" encoding=&qu ...

  2. Ubuntu 系统 update-rc.d 命令

    Ubuntu或者Debian系统中update-rc.d命令,是用来更新系统启动项的脚本.这些脚本的链接位于/etc/rcN.d/目录,对应脚本位于/etc/init.d/目录.在了解update-r ...

  3. 《InsideUE4》-9-GamePlay架构(八)Player

    你们对力量一无所知 引言 回顾上文,我们谈完了World和Level级别的逻辑操纵控制,如同分离组合的AController一样,UE在World的层次上也采用了一个分离的AGameMode来抽离了游 ...

  4. 用MonoDevelop开发Linux游戏

    鉴于微软将放弃XNA,MonoGame将是喜欢XNA的朋友们的新选择,他是XNA游戏引擎的开源实现,而且是跨平台的,这意味着你可以用他开发OS.android.windows以及linux应用程序,多 ...

  5. TCP协议的三次握手和四次挥手

    暂时需要的信息有: ACK : TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1 SYN(SYNchronization) : 在连接建立时用来同步序号.当SYN= ...

  6. [转]用Whois获得电信运营商的IP地址是如何分配的?

    [转]用Whois获得电信运营商的IP地址是如何分配的? Linux下获得一些中国电信运营商的IP地址分配情况: APNIC是管理亚太地区IP地址分配的机构,它有着丰富准确的IP地址分配库,同时这些信 ...

  7. gradle项目中如何支持java与scala混合使用?

    之前写过一篇maven项目中java与scala如何混用,今天来看看gradle项目中如何达到同样的效果: 一.目录结构 基本上跟maven一样,tips:这一堆目录结构不用死记,后面会讲如何用gra ...

  8. matlab 将多个盒图放在一张图上

    1.boxplot 将多个盒图放在一张图上 x1 = normrnd(5,1,100,1)';x2 = normrnd(6,1,200,1)';X = [x1 x2];G = [zeros(size( ...

  9. js精确加减乘除

    //加法函数function accAdd(arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[1 ...

  10. jQuery瀑布流简单示例

    1,以下demo是基于window的滚动