【30天熟悉Go语言】9 Go函数全方位解析

这篇具有很好参考价值的文章主要介绍了【30天熟悉Go语言】9 Go函数全方位解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者:秃秃爱健身,多平台博客专家,某大厂后端开发,个人IP起于源码分析文章 😋。
源码系列专栏:Spring MVC源码系列、Spring Boot源码系列、SpringCloud源码系列(含:Ribbon、Feign)、Nacos源码系列、RocketMQ源码系列、Spring Cloud Gateway使用到源码分析系列、分布式事务Seata使用到源码分析系列、JUC源码系列
基础系列专栏:30天熟悉GO语言(建设中)
码文不易,如果感觉博主的文章还不错,请点赞👍、收藏 ⭐️支持一下博主哇 🙏
联系方式:Saint9768,加我进技术交流群,一起学习进步📚、freedom✈️🌊

一、前言

【30天熟悉Go语言】9 Go函数全方位解析

Go系列文章:

  1. GO开篇:手握Java走进Golang的世界
  2. 2 Go开发环境搭建、Hello World程序运行
  3. 3 Go编程规约和API包
  4. 4 Go的变量、常量、运算符
  5. 5 Go 基本数据类型
  6. 6 Go 复杂数据类型之指针
  7. 7 Go流程控制之分支结构if、switch
  8. 8 Go流程控制之循环结构for range、goto、break、continue

Go专栏传送链接:https://blog.csdn.net/saintmm/category_12326997.html

二、函数

1、函数简介

1)函数是什么?

函数是一个基本的代码块,是用于完成某一功能的程序指令集;可以对特定的功能进行提取,形成一个代码片段,这个代码片段即函数。

我们可以通过函数划分不同的功能,逻辑上每个函数执行的是指定的任务。

2)为什么要使用函数?

提高代码的复用性,减少代码的冗余,提高代码的可维护性。

3)函数的特点?

  • 支持可变参数
  • 支持多返回值
  • 支持匿名函数
  • 函数也是一种数据类型,可以赋值给变量
  • 不支持函数的重载(overload),即:一个包不能有两个名字一样的函数
  • 不支持嵌套(nested),即:一个包不能有两个名字一样的函数

2、函数的声明和使用

1)函数声明

函数的定义格式如下:

func name( [parameter_list] ) [return_types] {
   函数体
}
  • 函数func关键字声明,包含一个函数名、参数列表、返回值列表和函数体。
    • 左大括号要和func关键字在一行,不能另起一行。
  • name:函数名称,只能定义一次,函数名称是唯一的,不支持重载
    • 函数名命名规范:驼峰命名
      • 首字母不能是数字;
      • 首字母大写该函数可以被本包文件和其它包文件使用(类似Java方法的public权限)
      • 首学母小写只能被本包文件使用,其它包文件不能使用(类似Java方法的private权限)
  • parameter_list:参数列表,指定参数类型、参数之间的顺序、参数个数;
    • 参数类型在参数名之后,函数可以没有参数、多个参数。
    • 当多个连续的参数是同一类型,除了最后一个参数写参数类型之外,可以参数都可以省略。
  • return_types:返回类型列表,函数可以返回任意数量的返回值;
    • 返回多个值时,多返回值必须用括号括起来,并以逗号隔开。
    • 返回单个值时,直接相应的返回值数据类型。
    • 如果函数没有返回值,返回类型列表可以省略。
  • 函数体:代码集合。

示例1:两数相加

func addAndFormat(num1, num2 int) int {
	sum := num1 + num2
	return sum
}

示例2:两数相加,返回相加结果和结果说明

func addAndFormat(num1, num2 int, format string) (int, string) {
	sum := num1 + num2
	return sum, fmt.Sprintf(format, sum)
}
函数名不支持重载

【30天熟悉Go语言】9 Go函数全方位解析

1> 返回多个值 – 返回值无名称

格式:

func name(arg1 T, arg2 T) (T, T) {
        ...
        return r1, r2
}

特点:

  • 当需要返回两个值及以上时,返回类型用小括号包裹,逗号分隔。
  • return语句中携带多个返回值。
2> 返回多个值 – 返回值有名称

格式:

func name(arg1 T, arg2 T) (r1 T, r2 T) {
        ...
        return
}

特点:

  • 返回值类型指定名称后,return语句中,可以不带值,也可以都带上。
  • 不过,当返回值有了名称之后,即使是只有一个返回类型,也需要用小括号包裹。

return可以不用携带值的原因?

  • 因为等价于在返回时,初始化好了返回值;
  • 以上面的格式来看,r1r2 是两个初始化的变量;
    • 在函数运算中,只要将返回结果存入r1r2中;
    • 如果不存,也可以选择在return语句携带多个返回值。

2)函数调用

函数可以被多次调用,在函数调用时传递的参数为实际参数(实参) ,其有具体的值,用来给函数形式参数(形参) 传递数据。

格式:

r1, r2 := name(param1, param2, param3)
  • 格式中,r1, r2表示两个返回值;param1, param2, param3表示三个参数。

如果接收多个值时,某个值不想使用,可以选择使用关键字_替代,表示不使用这个返回值。

r1, _ := name(param1, param2, param3)

以函数声明中的示例2为例:

1> 返回值全部接收:

package main

import "fmt"

func main() {
	format := "two num add, the result is : %d "
	sum, str := addAndFormat(1, 2, format)
	fmt.Println(sum)
	fmt.Println(str)

}

// 两数相加,返回相加结果和结果说明**
func addAndFormat(num1, num2 int, format string) (int, string) {
	sum := num1 + num2
	return sum, fmt.Sprintf(format, sum)
}

控制台输出:
【30天熟悉Go语言】9 Go函数全方位解析

2> 只接收一个返回值:

package main

import "fmt"

func main() {
	format := "two num add, the result is : %d "
	sum, _ := addAndFormat(1, 2, format)
	fmt.Println(sum)
}

3、函数参数

1)值传递

Go语言中,基本数据类型和数组默认都是值传递的,即:进行值拷贝;在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改时,将不会影响到原来的值。

示例:定义一个swap函数,用于交换两个int类型数据的值

package main

import "fmt"

func main() {
	a := 3
	b := 6
	fmt.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)
}

// 两值交换
func swap(x, y int) int {
	var temp int

	temp = x
	x = y
	y = temp

	return temp
}

控制台输出:
【30天熟悉Go语言】9 Go函数全方位解析

调用swap()函数交换a,b值,交换前后 a,b的值没有改变;

所以值传递不会改变传入函数实参的值,它只是复制一份数据用于函数体的执行。

2)引用传递

如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址 &,函数内以指针的方式操作变量;从效果来看类似引用传递

示例:

package main

import "fmt"

func main() {
	a := 3
	b := 6
	fmt.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)
}

// 两值交换
func swap(x, y *int) int {
	var temp int

	temp = *x
	// 将y地址上的值复制到x的地址上
	*x = *y
	*y = temp

	return temp
}

控制台输出:
【30天熟悉Go语言】9 Go函数全方位解析

PS:

  • 无论是值传递,还是引用传递,传递给函数的都是变量的副本;不过,值传递的是值的拷贝,引用传递的是地址的拷贝。
    • 一般而言,地址拷贝更为高效。值拷贝的效率取决于拷贝对象的大小,对象越大,性能越低。
  • map、slice、chan、指针、interface默认以引用的方式传递。

3)不定参数 / 可变参数

不定参数传值指:函数的参数类型固定、参数数量不固定;Go语言中可变参数本质上是 slice,并且该 slice 只能有一个,且必须是最后一个。

// 0个或多个参数
func myfunc1(args ...int) {
}

// 1个或多个参数
func add(a int, args…int) int {
}

// 2个或多个参数
func add(a int, b int, args…int) int {
}
  • 注意:其中 args 是一个slice,我们可以通过 arg[index] 依次访问所有参数,通过 len(arg) 来判断传递参数的个数;

4、函数嵌套/递归使用

1)函数嵌套使用

函数中调用其他函数。
示例:test函数中调用test1函数

package main

import "fmt"

func main() {
	test(3, 6)
}

func test1(a, b int) {
	fmt.Println(a + b)
}
func test(a int, b int) {
	fmt.Println("into function test")
	test1(a, b)
}
不定参不能直接传递给另外一个不定参

【30天熟悉Go语言】9 Go函数全方位解析

解决措施:指定不定参arr需要传递到函数testIndefiniteParam2的数据个数;

1> 指定个数:

package main

import "fmt"

func main() {

	testIndefiniteParam(1, 2, 3, 4)
}

// 不定参不能直接传递给另外一个不定参
func testIndefiniteParam(arr ...int) {
	// 传递指定个数的数据,不报错
	// 下标起始0,结束4(不包含4);仅仅为索引4之前的数据
	testIndefiniteParam2(arr[0:4]...)
}
func testIndefiniteParam2(arr ...int) {
	fmt.Println(arr)
}

输出:
【30天熟悉Go语言】9 Go函数全方位解析
2> 若想要传递全量数据,可以用下列方式:

testIndefiniteParam2(arr[0:]...)

2)递归函数

递归指函数在运行的过程中自己调用自己。

构成递归的条件:

  1. 子问题需要与原始问题是同样的问题,且更加简单;
  2. 递归需要出口,不能无限制地调用本身;在使用递归时,开发者需要设置 退出条件 ,否则递归将陷入无限循环。

示例:斐波那契数列(fibonacci)

package main

import "fmt"

func fibonacci(i int) int {
	if i == 0 {
		return 0
	}
	if i == 1 {
		return 1
	}
	return fibonacci(i-1) + fibonacci(i-2)
}

func main() {
	var i int
	for i = 0; i < 10; i++ {
		fmt.Printf("%d\n", fibonacci(i))
	}
}

控制台输出:
【30天熟悉Go语言】9 Go函数全方位解析

5、匿名函数

匿名函数指不需要定义函数名的一种函数实现方式,匿名函数由一个不带函数名的函数声明和函数体组成;在Go语言中,函数可以像普通变量一样被传递 & 使用,并且支持随时在代码里定义匿名函数。

构造函数时,函数没有名称;想调用函数时,需要把匿名函数赋值给一个变量,或 在构造时直接调用。

1)赋值给变量

func1 := func (arg1 T, arg2 T) T {
        ...
        return r1
}

这里,func1是一个函数类型的变量,可以直接通过func1(param1, param2)调用函数。

示例:

package main

import "fmt"

func main() {

	// 1> 匿名函数赋值给变量
	func1 := func (a int, b int) int {
		res := a + b
		fmt.Printf("a + b = %d \n", res)
		return res
	}
	func1(2,3)
}

2)构造时直接调用

func (arg1 T, arg2 T) T {
        ...
        return r1
}(param1, param2)

构造函数时,函数声明的右大括号}后紧跟要传递的参数(param1, param2);这样,构造完函数后会马上调用。

示例:

package main

import "fmt"

func main() {

	//2> 匿名函数构造时直接调用
	func (a int, b int) int {
		res := a + b
		fmt.Printf("a + b = %d \n", res)
		return res
	}(2, 3)
}

6、函数类型

在Go语言中,函数也是一种数据类型,可以将函数赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用。(匿名函数中有介绍

所谓的声明函数类型,实际就是为已存在的函数起一个别名。尤其当我们引用二方包、三方包时,可以给别人的函数取个别名。

语法:

type 自定义数据类型名 func ( [parameter_list] ) [return_types]

示例:

package main

import "fmt"

// 定义函数类型 为已存在的数据类型起别名
type funcTwoParamOneReturn func(int, int) int

func main() {
	// 自定义函数类型使用
	var f funcTwoParamOneReturn
	f = func2
	fmt.Printf("%T", f)
}

// func(int, int)
func func1(a int, b int) {
	fmt.Println(a + b)
}

// func(int, int) int
func func2(x int, y int) int {
	fmt.Println(x + y)
	return x + y
}

对已有函数设置别名时,函数的参数类型/个数、返回值类型/个数 需要一一对应,否则会报错;
【30天熟悉Go语言】9 Go函数全方位解析

7、Go内置函数

和Java语言一样,在Go语言中,有一些函数无需导包即可使用,这样的内置函数有15个:

  1. make:为切片,map、通道类型分配内存并初始化对象。
  2. len:计算数组、切片、map、通道的长度。
  3. cap:计算数组、切片、通道的容量。
  4. delete:删除 map 中对应的键值对。
  5. append:将数据添加到切片的末尾。
  6. copy:将原切片的数据复制到新切片中。
  7. new:除切片、map、通道类型以外的类型分配内存并初始化对象,返回的类型为指针。
  8. complex:生成一个复数。
  9. real:获取复数的实部。
  10. imag:获取复数的虚部
  11. print:将信息打印到标准输出,没有换行。
  12. println:将信息打印到标准输出并换行。
  13. close:关闭通道。
  14. panic:触发程序异常。
  15. recover:捕捉 panic 的异常信息,必须卸载defer相关的代码块中。
    • defer将在后面的文章中介绍

三、总结

本文介绍了函数的一些基本概念,比如:函数是什么?为什么要使用函数?函数的特点?怎么声明一个函数?如何调用一个函数?嵌套函数是什么?匿名函数怎么声明使用?Go中内置函数有哪些?

对比Java来看

和Java语言一样:

  • 基础类型都是按值传递,复杂类型都是按引用传递;
    *Go中基础类型如需按引用传递,需要使用指针。
  • 都可以丢弃返回值。Go中使用关键字_接收返回值用于丢弃,Java直接不用变量接方法的返回。

和Java语言不同的是:

  • Go中返回值可以有多个,Java中多个值需要封装到实体或map返回;
  • Go中的函数不支持重载,而Java方法可以重载;

Java中的方法定义:

访问修饰符 返回值类型 方法名(参数1类型  参数1,参数2类型 参数2...) {
    return 返回值;
}

Go中的函数定义:文章来源地址https://www.toymoban.com/news/detail-495854.html

func 函数名(变量1 变量类型,变量2 变量2类型...) (返回值1 类型1,返回值2 类型2...) {
    return;
}

到了这里,关于【30天熟悉Go语言】9 Go函数全方位解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kotlin全方位-简单解析

    Kotlin是一种现代化的静态类型编程语言,由JetBrains公司开发。它可以在Java虚拟机(JVM)上运行,并且可以与Java代码无缝地进行互操作。Kotlin旨在提供更简洁、更安全、更具表达力和更高效的编程语言。 Android开发:Kotlin被广泛用于Android应用程序的开发。它可以与Java代码互操

    2024年02月10日
    浏览(58)
  • Java——线程睡眠全方位解析

    在 Java 中,让线程休眠的方法有很多,这些方法大致可以分为两类,一类是设置时间,在一段时间后自动唤醒,而另一个类是提供了一对休眠和唤醒的方法,在线程休眠之后,可以在任意时间对线程进行唤醒。 线程睡眠的方法有以下 5 个: Thread.sleep TimeUnit wait Condition LockSu

    2024年02月04日
    浏览(58)
  • 【全方位解析】如何写好技术文章

    前言 为何而写 技术成长 :相对于庞大的计算机领域的知识体系,人的记忆还是太有限了,而且随着年龄的增大,记忆同样也会逐渐衰退,正如俗话所说“好记性不如烂笔头”。并且在分享博客的过程中,我们也可以和大神交流,进而发现自己的认知错误,纠正知识体系。最

    2024年02月16日
    浏览(50)
  • 【全方位解析】如何获取客户端/服务端真实 IP

    1.比如在投票系统开发中,为了防止刷票,我们需要限制每个 IP 地址只能投票一次 2.当网站受到诸如 DDoS(Distributed Denial of Service,分布式拒绝服务攻击)等攻击时,我们需要快速定位攻击者 IP 3.在渗透测试过程中,经常会碰到网站有 CDN(Content Distribution Network,内容交付网络

    2024年02月07日
    浏览(58)
  • Python教程(24)——全方位解析Python中的装饰器

    Python装饰器是一种特殊的函数,它接收一个函数作为参数,然后返回一个新的函数,用于扩展或修改原始函数的行为。装饰器提供了一种便捷的方式来在不修改被装饰函数源代码的情况下,增加、修改或包装函数的功能。通俗点说就是尽量不修改原有功能代码的情况下,给原

    2024年02月02日
    浏览(60)
  • 【30天精通Zabbix:从入门到实战的全方位监控之旅】第1天:初探Zabbix:开源监控系统的王者

    🌟 热烈欢迎 踏上这30天Zabbix学习之旅的每一位朋友!能在这里和大家相聚,我真的感到非常开心与期待!🎉 🎈 今天,我们将共同启程,去探寻在开源监控领域中独领风骚的王者——Zabbix。不论您是维护系统稳定的管理员、还是日夜兼程保障运行的运维工程师,又或是对监

    2024年03月19日
    浏览(49)
  • 深度解析知网AIGC检测:从理论到实践,全方位探索前沿技术

    大家好,小发猫降ai今天来聊聊深度解析知网AIGC检测:从理论到实践,全方位探索前沿技术,希望能给大家提供一点参考。降ai辅写 以下是针对论文AI辅写率高的情况,提供一些修改建议和技巧,可以借助此类工具: 还有: 标题: \\\"深度解析知网AIGC检测:从理论到实践,全方

    2024年03月16日
    浏览(67)
  • 一文章让你彻底了解OpenAI:CSDN独家全方位解析

    目录 什么是OpenAI OpenAI的发展历程 相关名词解释 API​ GPT​ GPT-2​ GPT-3​

    2024年02月09日
    浏览(60)
  • C++环形缓冲区设计与实现:从原理到应用的全方位解析

    环形缓冲区(Circular Buffer),也被称为循环缓冲区(Cyclic Buffer)或者环形队列(Ring Buffer),是一种数据结构类型,它在内存中形成一个环形的存储空间。环形缓冲区的特点是其终点和起点是相连的,形成一个环状结构。这种数据结构在处理流数据和实现数据缓存等场景中具

    2024年02月07日
    浏览(61)
  • 深入浅出 C 语言:学变量、掌控流程、玩指针,全方位掌握 C 编程技能

    C 语言介绍 C 语言的特性 C 语言相对于其他语言的优势 C 程序的编译 C 中的 Hello World 程序 参考文章: C 语言入门:如何编写 Hello World C 语言函数:入门指南 C 中的变量和 C 语言中的作用域规则 C 中的数据类型 运算符及其类型 C 语言中的类型转换 参考文章: C 语言注释

    2024年02月02日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包