一、 函数介绍

1.1 定义

函数:有输入、有输出,用来执行一个指定任务的代码块。

func functionname([parametername type]) [return type] {
//function body
} //其中参数列表和返回值列表都是可选的

解释:

func 函数名([参数名 类型])[返回值 类型] {
函数体
}

1.2 特点

golang函数的特点:

1)不支持重载,即一个包不能有两个名字一样的函数

2)函数也是一种类型,一个函数可以赋值给变量

3)匿名函数

4)多返回值

1.3无参数和无返回值的函数

func functionname() {
//function body
}

1.4 小练习 实现两个数相加

func add(a int, b int) int {
Return a + b
}

1.5 如果连续的一系列参数的类型是一样,前面的类型可以不写

func add(a, b int) int {
Return a + b
}

1.6 函数调用

函数隶属于同一个包:

func add(a, b int) int {
Return a + b
}
func main() {
sum := add(, )
}

函数隶属于不同的包:

func add(a, b int) int {
Return a + b
}
func main() {
sum := 包名.add(, )
}

1.7 注意

函数传参,传递的是值的副本。

只不过值类型传递的是值,指针类型(引用类型)传递的是内存地址

二、 多返回值和可变参数

2.1 多返回值

package main

import "fmt"

func calc(a, b int) (int, int) {
sum := a + b
sub := a - b
return sum, sub
}
func main() {
sum, sub := calc(, )
fmt.Println(sum, sub)
}

2.2 对返回值进行命名

package main

import "fmt"

func calc(a, b int) (sum, sub int) {
sum = a + b
sub = a - b
return
}
func main() {
sum, sub := calc(, )
fmt.Println(sum, sub)
}

实例2-1 活学活用

package main

import (
"fmt"
) // Calc 函数
func Calc(a, b int) (int, int) {
return a + b, a - b
} // Calc2 函数
func Calc2(a, b int) (s1, s2 int) {
s1 = a + b //这里直接赋值,不在定义s1变量是因为上面函数返回值那里已经定义了s1变量并且为int类型
s2 = a - b
return
} // Calc3 函数
func Calc3(a, b int) (int, int) {
s1 := a + b
s2 := a - b
return s1, s2
} func main() {
var sum int
var sub int
sum, sub = Calc(, )
sum1, sub1 := Calc2(, )
sum2, sub2 := Calc3(, )
fmt.Printf("sum:%d sub:%d sum1:%d sub1:%d sum2:%d sub2:%d", sum, sub, sum1, sub1, sum2, sub2)
}

执行结果如下:

2.3 _标识符

当我们函数有2个返回值,但是我们只想输出其中一个,这时候就要用到_占位符拉进行忽略掉

实例2-2

package main

import (
"fmt"
) func calc(a, b int) (sum int, sub int) {
sum = a + b
sub = a - b
return
}
func main() {
sum, _ := calc(, )
fmt.Println(sum)
}

执行结果如下:

2.4 可变参数

变长函数被调用的时候可以有可变的参数个数;

在参数列表最后的类型名称前使用省略号...可以声明一个变长的函数;

可变参数可传递0个或多个参数,其在底层其实就是一个切片

例如:

0个或多个参数
func add(arg ...int) int{ }
1个或多个参数
func add(a int,arg ...int) int{ }
2个或多个参数
func add(a int,b int,arg ...int)int{ }

或者

func calc_v1(b …int) (sum int, sub int) {  //0个或多个参数
return
}
func calc_v2(a int, b …int) (sum int, sub int) { //1个或多个参数
return
}
func calc_v3(a int, b int, c …int) (sum int, sub int) { //2个或多个参数
return
}

实例2-3

package main

import (
"fmt"
) func Add(a ...int) int { //边长参数a,需要传入0个或多个参数
fmt.Printf("func args count:%d\n", len(a)) //变长参数在底层存储就是一个切片,计算切片的长度,其实就是计算传入参数的个数
var sum int
for index, args := range a { //遍历切片,打印出传入参数值
fmt.Printf("args[%d]=%d\n", index, args)
sum = sum + args //求和
}
return sum
} func testAdd() {
sum := Add() //传入0个参数
fmt.Printf("sum=%d\n", sum) sum = Add() //传入1个参数
fmt.Printf("sum=%d\n", sum) sum = Add(, ) //传入2个参数
fmt.Printf("sum=%d\n", sum)
}
func main() {
testAdd()
}

执行结果如下:

三、 defer语句

3.1 defer

特性:

1)在函数退出时去执行

2)defer语句经常使用成对的操作,比如打开和关闭,连接和断开,加锁和解锁

defer用途

1)当函数返回时,执行defer语句,因此可以用来做资源清理

2)多个defer语句,按先进后出的方式执行

3)defer语句中的变量,在defer声明时就决定了

实例3-1

下面拿关闭一个打开文件操作为例子,当我们通过os.Open()打开一个文件的时候可以在后面添加defer f.Close() 这样在函数结束时就可以帮我们自动关闭一个打开的文件

package main

import (
"fmt"
"os"
) func testDefer() {
file, err := os.Open("C:/tmp.txt") //os也是go语言标准包(用于操作系统),打开一个文件会返回2个值,第一个是文件打开成功,会返回一个文件的对象,通过这个文件对象,就可以去操纵这个文件了(打开、修改等),第二个返回的是错误信息
if err != nil {
fmt.Printf("open file failed,err:%v\n", err)
return
} defer file.Close() //这里我们使用defer语句,因为defer语句是在函数退出时最后执行,所以当我们文件对象成功生成后,在这里加上该条语句,那么不论下面哪一条语句成功执行要退出函数时,都会在退出函数时执行该语句,这样就不需要我们去在每一条语句写file.close了。 var buf []byte
n, err := file.Read(buf[:]) //要传的是切片,如果是数组,值类型改不了外面真正的值,改的是副本。
if err != nil {
fmt.Printf("read file failed,err:%v\n", err)
//file.Close() //文件成功打开后需要关闭
return
}
fmt.Printf("read %d byte succ, content:%v\n", n, string(buf[:]))
//file.Close() //文件成功打开后需要关闭
return
}
func main() {
testDefer()
}

执行结果如下:

3.2 多个defer语句

多个defer语句,遵循栈的特性:先进后出。

见如下实例:

实例3-2

package main

import (
"fmt"
) func main() {
defer fmt.Println("hello world")
defer fmt.Println("hello world2")
defer fmt.Println("hello world3")
}

执行结果如下:

四、 内置函数

1. close:主要用来关闭channel

2. len:用来求长度,比如string、 array、 slice、 map、 channel

3. new:用来分配内存,主要用来分配值类型,比如int、 struct。返回的是指针

4. make:用来分配内存,主要用来分配引用类型,比如chan、 map、 slice

5. append:用来追加元素到数组、 slice中

6. panic和recover:用来做错误处理

五、 变量作用域

5.1 全局变量

也就是在函数外面的变量,被称为全局变量,该变量对整个程序都生效。

例如:下面例子a变量在全局都是生效的

var a int = 

func test() {

}

func main() {

}

5.2 局部变量

局部变量,分为两种:

1)函数内定义

仅仅在变量所定义的函数内生效

2)语句块内定义。

仅仅在变量所定义的语句块内生效

理解见如下例子:

func add(a int, b int) int {
var sum int =
//sum是局部变量
if a > {
var c int =
//c是布局变量,尽在if语句块有效
}
}

5.3 可见性

之前我们已经学过了针对函数的首字母大小写,函数名首字母大写,意味着其是可以导出的,能够被其他包访问或调用。函数名首字母小写表示是私有的,不能被外部的包访问调用。

这里,变量也是和函数一样,遵循一样的规则

可见性,包内任何变量或函数都是能访问的。包外的话,变量首字母大写是可导出的

能够被其他包访问或调用。变量小写表示是私有的,不能被外部的包访问。

实例5-1         测试变量在包内

package main

import (
"fmt"
) var (
a =
A =
) func main() {
fmt.Println(a, A)
}

执行结果如下:

实例5-2         测试变量在包外

test包代码

package test

var (
b =
B =
)

主程序main包代码

包外变量小写b:

package main

import (
"fmt"
"three/test"
) func main() {
fmt.Println(test.b)
}

执行结果:直接报b变量未定义

包外变量大写B:

package main

import (
"fmt"
"three/test"
) func main() {
fmt.Println(test.B)
}

执行结果:

六、 匿名函数

6.1 声明

func(参数列表) 返回值列表 {

      函数体...

}

6.2 应用场景

1)函数也是一种类型,因此可以定义一个函数类型的变量,也就是匿名函数,通过将匿名函数赋值给变量,这样我们就可以一直使用了(匿名函数可以直接赋值给一个变量或直接执行)。

2)匿名函数,也就是没有名字的函数。

针对上述2点通过如下实例进行解释:

实例6-1

package main

import (
"fmt"
) func testA1() {
sum := func(a, b int) int { //将匿名函数赋值给变量
sum := a + b
return sum
}(, )
fmt.Printf("sum = %d\n", sum)
} func testA2() {
f1 := func(a, b int) int {
sum := a + b
return sum
} s1 := f1(, )
s2 := f1(, ) //因为是变量,所以多次调用
fmt.Printf("s1=%d,s2=%d\n", s1, s2)
} func main() {
testA1()
testA2()
}

执行结果如下:

3)defer中使用匿名函数

实例6-2

package main

import (
"fmt"
"os"
) func testDefer() {
file, err := os.Open("C:/Go/robots.txt")
/* 写法1:
defer func(f *os.File) { //函数声明参数名和类型
if f != nil {
f.Close()
}
}(file)
*/
//写法2
defer func() { //匿名函数配合闭包使用匿名函数外的变量file
if file != nil {  //file其实就是指针类型,如果不判断是否为空,会出问题(如果file是空,flie.close()调用就会报错)
file.Close()
}
}() if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
} //defer file.Close() var buf []byte
n, err := file.Read(buf[:])
if err != nil {
fmt.Printf("read file failed, err:%v\n", err)
//file.Close()
return
} fmt.Printf("read %d byte succ, content:%s\n", n, string(buf[:]))
//file.Close()
return
} func main() {
testDefer() }

因为之前已经用过这个例子,所以此处就不在贴出执行结果了。重点是体会defer中使用匿名函数。

4)函数作为一个参数

实例6-3

package main

import (
"fmt"
) func calc(op func(args ...int) int, op_args ...int) int { //第一个参数op是一个函数类型的参数,函数是一个接收可变类型参数,有一个返回值;第二个参数直接是一个可变参数
result := op(op_args...) //将第2个参数op_args传给op函数,并且是切片,要传递需要将切片展开,也就是切片名...
fmt.Printf("result = %d\n", result)
return result
} func add(args ...int) int {
var sum int
for i := ; i < len(args); i++ {
sum = sum - args[i]
fmt.Println(i)
}
return sum
} func main() {
calc(func(args ...int) int { //因为参数类型是匿名函数,所以我们此处调用传参,也需要传入匿名函数
//return 0 //1就是op的返回值
var sum int
for i := ; i < len(args); i++ { //这里args的参数其实就是calc函数中第二个可变参数的值(因为上面op函数已经将可变(切片)参数值展开了)
sum = sum + args[i]
fmt.Println(i)
}
return sum
}, , , , , ) calc(func(args ...int) int { //因为参数类型是匿名函数,所以我们此处调用传参,也需要传入匿名函数
//return 0 //1就是op的返回值
var sum int
for i := ; i < len(args); i++ {
sum = sum - args[i]
}
return sum
}, , , , , ) calc(add, , ) //不单单是匿名函数可以传参,有名函数传的值、类型和给的返回值是同一类型,其就是同一类型函数
}

执行结果如下:

七、 闭包

7.1 定义

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

下面来看一个demo来理解一下闭包:

func f(i int) func() int {
return func() int {
i++
return i
}
}

函数f返回了一个函数,返回的这个函数就是一个闭包。这个函数中本身是没有定义变量i的,而是引用了它所在的环境(函数f)中的变量i,这个变量i就和匿名函数打包为一个整体,在闭包的这个有效区间内一直是生效存在的。

7.2 实例1

package main

import "fmt"

func main() {
var f = Adder() //f就是一个闭包实例
fmt.Print(f(), "-") // x=0 d=1 x=0+1=1
fmt.Print(f(), "-") //x=1 d=20 x=1+20=21
fmt.Print(f()) //x=21 d=300 x=21+300=321
}
func Adder() func(int) int {
var x int //x未赋值,默认为0
return func(d int) int {
x += d //匿名函数引用外部函数的x变量,x就相当于匿名函数中的全局变量了,不在是Adder函数中的局部变量了。
return x
}
}

执行结果如下:

注意:f(1),这个传值1是传给返回值匿名函数的参数d的。

7.3 实例2

package main

import (
"fmt"
) func Adder() func(int) int {
var x int
return func(d int) int {
x = x + d
return x
}
} func main() {
f := Adder()
fmt.Println(f())
fmt.Println(f()) f1 := Adder() //Adder函数重新做了实例化
fmt.Println(f1())
fmt.Println(f1())
}

执行结果如下:

7.4 实例3

package main

import "fmt"

func add(base int) func(int) int { //base在闭包中是全局变量
return func(i int) int {
base += i
return base
}
}
func main() {
tmp1 := add() //base=10
fmt.Println(tmp1(), tmp1()) //i=1 base=10+1=11 i=2 base=11+2=13
tmp2 := add() //base=100
fmt.Println(tmp2(), tmp2()) //i=1 base=100+1=101 i=2 base=101+2=103
}

执行结果如下:

7.5 实例4

package main

import (
"fmt"
"strings"
) func makeSuffixFunc(suffix string) func(string) string { //suffix是全局变量
return func(name string) string {
if !strings.HasSuffix(name, suffix) { //string.HasSuffix(name,suffix) 表达的是:name以suffix为结尾返回true
return name + suffix
}
return name
}
}
func main() {
func1 := makeSuffixFunc(".bmp") //suffix=.bmp
func2 := makeSuffixFunc(".jpg") //suffx=.jpg
fmt.Println(func1("test")) //name=test
fmt.Println(func2("test")) //name=test
}

执行结果如下:

7.6 实例5

package main

import "fmt"

func calc(base int) (func(int) int, func(int) int) { //base是全局变量 ,返回值是2个匿名函数
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add, sub
}
func main() {
f1, f2 := calc() //f1和f2引用的是同一个base变量(base=10),相当于base变量在add和sub两个匿名函数内都是生效的,是同一个。
fmt.Println(f1(), f2()) //f1:base=10 i=1 base=10+1=11 f2:base=11 i=2 base=11-2=9
fmt.Println(f1(), f2()) //f1:base=9 i=3 base=9+3=12 f2:base=12 i=4 base=12-4=8
fmt.Println(f1(), f2())
fmt.Println(f1(), f2())
}

执行结果如下:

Go语言基础之7--函数详解的更多相关文章

  1. 【opencv基础】detectmultiscale函数详解

    前言 简单的人脸检测程序可以直接基于opencv的函数库进行实现,本文介绍一下detectMultiScale函数. 函数简介 opencv2人脸检测使用的是detectMultiScale函数,可以 ...

  2. python基础-内置函数详解

    一.内置函数(python3.x) 内置参数详解官方文档: https://docs.python.org/3/library/functions.html?highlight=built#ascii ...

  3. 【C语言】printf函数详解

    C语言printf函数详解 一.相关基础知识 请求printf()打印变量的指令取决于变量的类型,例如打印整数用%d符号,打印字符用%c符号,这些符号称为转换说明(conversion specifi ...

  4. 【转载】C语言itoa()函数和atoi()函数详解(整数转字符C实现)

    本文转自: C语言itoa()函数和atoi()函数详解(整数转字符C实现) 介绍 C语言提供了几个标准库函数,可以将任意类型(整型.长整型.浮点型等)的数字转换为字符串. int/float to ...

  5. C语言memset函数详解

    C语言memset函数详解 memset() 的作用:在一段内存块中填充某个给定的值,通常用于数组初始化与数组清零. 它是直接操作内存空间,mem即“内存”(memory)的意思.该函数的原型为: # ...

  6. python基础之函数详解

    Python基础之函数详解 目录 Python基础之函数详解 一.函数的定义 二.函数的调用 三.函数返回值 四.函数的参数 4.1 位置参数 4.2 关键字参数 实参:位置实参和关键字参数的混合使用 ...

  7. Netsuite Formula > Oracle函数列表速查(PL/SQL单行函数和组函数详解).txt

    PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类: 单行函数 ...

  8. Qt零基础教程(四)QWidget详解(3):QWidget的几何结构

    Qt零基础教程(四)  QWidget详解(3):QWidget的几何结构 这篇文章里面分析了QWidget中常用的几种几何结构 下图是Qt提供的分析QWidget几何结构的一幅图,在帮助的 Wind ...

  9. windows socket函数详解

    windows socket函数详解 近期一直用第三方库写网络编程,反倒是遗忘了网络编程最底层的知识.因而产生了整理Winsock函数库的想法.以下知识点均来源于MSDN,本人只做翻译工作.虽然很多前 ...

随机推荐

  1. 使用广播退出打开了多个activity的程序

    新建一个父类,在父类里动态注册广播,在这个广播的onrecive方法中结束当前activity,让每个activity继承这个父类,在要关闭的activity中发送广播,搞定 下面是代码 父类 pro ...

  2. 客户注册功能,发短信功能分离 通过ActiveMQ实现

    客户注册功能,发短信功能分离 通过ActiveMQ 配置链接工厂, 配置session缓存工厂(引入链接工厂) 2.配置模板对象JmsTemplate 引入缓存工厂    指定消息模式(队列,发布和订 ...

  3. Zbar 大图像分析

    博客转载自:https://blog.csdn.net/sunflower_boy/article/details/50429252 为了减少处理时间,可以设定更大的扫描间距,减少不必要的解码类型,去 ...

  4. 算法Sedgewick第四版-第1章基础-009一链表与数组的比较及其他数据结构

    1. 2.

  5. 利用HTML5 与CSS3 做的放大镜

    利用HTML5 与CSS3 做的放大镜 html结构 <div class="wrap"> <div class="move"> < ...

  6. 使用 classList API

    一.classList API 是什么 属于 DOM API,HTML5 引入,用来操作 HTML 标签的 class 属性值. classList 属性是一个只读的类数组对象,"实时&qu ...

  7. 《Effective Java》第11章 序列化

    "将一个对象编码成一个字节流",称作将该对象序列化(serializing); 相反的处理过程被称作反序列化(deserializing),一旦对象被序列化后,它的编码就可以从一台 ...

  8. wifi下远程连接Android设备方法

    问题描述: android开发真机调试过程中,我们总是会重复卸载.安装这两个过程进行调试,通常都是用数据线连接电脑,能否摆脱数据线呢? 无线调试: 前提条件,电脑和手机必须处于同一局域网. 1.手机通 ...

  9. 【Arcgis for android】Error inflating class com.esri.android.map.MapView【已解决】

    解决方案:如果你是一个项目之前调试是好的,突然调试报这个错,听我的,直接卸载手机上调试的这个程序,重新调试,你会发现ok了 环境:arcgis android 10.2 错误:E/AndroidRun ...

  10. Centos7安装配置JDK8

    Centos7安装配置JDK8 一.准备工作 第一步,去甲骨文官网下载Jdk相应的版本,我这里下载的是jdk1.8. 第二步将你从官网上下载下来的jdk使用FTP工具上传到云服务器上的相应目录,我的是 ...