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

news2024/9/17 7:48:24

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

文章目录

  • 一、前言
  • 二、函数
    • 1、函数简介
      • 1)函数是什么?
      • 2)为什么要使用函数?
      • 3)函数的特点?
    • 2、函数的声明和使用
      • 1)函数声明
        • 函数名不支持重载
        • 1> 返回多个值 -- 返回值无名称
        • 2> 返回多个值 -- 返回值有名称
      • 2)函数调用
    • 3、函数参数
      • 1)值传递
      • 2)引用传递
      • 3)不定参数 / 可变参数
    • 4、函数嵌套/递归使用
      • 1)函数嵌套使用
        • 不定参不能直接传递给另外一个不定参
      • 2)递归函数
    • 5、匿名函数
      • 1)赋值给变量
      • 2)构造时直接调用
    • 6、函数类型
    • 7、Go内置函数
  • 三、总结
    • 对比Java来看

一、前言

在这里插入图片描述

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)
}

函数名不支持重载

在这里插入图片描述

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)
}

控制台输出:
在这里插入图片描述

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
}

控制台输出:
在这里插入图片描述

调用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
}

控制台输出:
在这里插入图片描述

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)
}

不定参不能直接传递给另外一个不定参

在这里插入图片描述

解决措施:指定不定参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)
}

输出:
在这里插入图片描述
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))
	}
}

控制台输出:
在这里插入图片描述

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
}

对已有函数设置别名时,函数的参数类型/个数、返回值类型/个数 需要一一对应,否则会报错;
在这里插入图片描述

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中的函数定义:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/674736.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【好书精读】网络是怎样连接的 之 数据收发完成之后 从服务器断开并删除套接字

&#xff08; 该图由AI制作 &#xff09; 目录 数据收发完成后协议栈要执行的操作 数据发送完毕后断开连接 删除套接字 数据收发操作小结 第一步是创建套接字 然后 客户端会向服务器发起连接操作 数据收发阶段 执行断开操作 数据收发完成后协议栈要执行的操作 数据发…

html_css模拟端午赛龙舟运动

文章目录 ⭐前言&#x1f496; 样式布局&#x1f496; 添加龙舟&#x1f496; 添加css_animation运动 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本期给大家分享css实现赛龙舟运动。 &#x1f496; 样式布局 风格&#xff1a;卡通 首先采用一张包括水元素的照片…

液体泄露识别检测算法 监控识别管道液体泄漏

液体泄露识别检测算法通过 yolov8python网络模型技术&#xff0c;液体泄露识别检测算法对管道的液体泄露情况进行全天候不间断实时监测&#xff0c;检测到画面中管道设备液体泄露现象时&#xff0c;将自动发出警报提示相关人员及时采取措施。YOLOv8 算法的核心特性和改动可以归…

硬件入门之什么是二级管

硬件入门之什么是二级管 文章目录 硬件入门之什么是二级管一、二极管是什么&#xff1f;二极管在电路中的作用很广泛&#xff1a; 单向导电&#xff0c;续流&#xff0c;稳压等等 二、分类二极管实际应用场景二极管分类1.通用二极管2..肖特二极管 3.稳压二极管&#xff08;利用…

零基础学会python编程——输入 / 输出函数与变量

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 学习目标 一.输入与输出函数 1.print 函数 【例2-1】 【例2-2】 【例2-3】 …

深度详解Nginx正向代理与反向代理

正向代理和反向代理的概念 1、正向代理2、反面代理以租房为例解释正向代理和反向代理&#xff1f;正向代理和反向代理配置 1、正向代理 它的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器&#xff0c;这个代理服务器呢,他能访问那个…

一文弄懂git原理及相关操作

文章目录 前言Git 是什么&#xff1f;Git 与 SVN 的区别Git 快速入门Git 相关网站Git 工作流程Git 工作空间Git 文件状态 Git 安装在 Linux 上安装在 Mac 上安装在 Windows 上安装从源代码安装 Git 配置Git 常用命令git initgit clonegit addgit commitgit branchgit tag查看信…

字符设备驱动内部实现原理解析

字符设备驱动内部实现原理解析 一. 字符设备驱动对象内部实现原理解析二. 字符设备驱动的注册流程三. 代码示例 一. 字符设备驱动对象内部实现原理解析 用户层&#xff1a; ​ 当用户打开&#xff08;open&#xff09;一个文件时,会生成一个文件描述符表 内核层&#xff1a; 内…

极致呈现系列之:Echarts散点图的数据魔力

目录 什么是散点图散点图的特点及应用场景散点图的特点散点图的应用场景 Echarts中散点图的常用属性Vue3中创建散点图美化散点图样式 在数据分析和可视化过程中&#xff0c;散点图是一种常见且重要的工具。散点图可以帮助我们直观地观察两个变量之间的关系&#xff0c;并发现其…

MySQL实战解析底层---“order by“是怎么工作的

目录 前言 全字段排序 rowid排序 全字段排序 VS rowid排序 前言 在开发应用的时候&#xff0c;一定会经常碰到需要根据指定的字段排序来显示结果的需求以举例市民表为例&#xff0c;假设你要查询城市是“杭州”的所有人名字&#xff0c;并且按照姓名排序返回前1000个人的姓…

20230622作业:字符设备驱动内部实现原理及流程

1.1字符设备驱动内部实现原理 1>用户打开设备open("~/dev/mycdev",O_RDWR);("路径"&#xff0c;打开方式)2>在内核的虚拟文件系统层会同步执行sys_open函数,实现如下操作3>根据open函数的路径&#xff0c;找到struct inode结构体4>在struct…

深入理解深度学习——GPT(Generative Pre-Trained Transformer):GPT-3与Few-shot Learning

分类目录&#xff1a;《深入理解深度学习》总目录 相关文章&#xff1a; GPT&#xff08;Generative Pre-Trained Transformer&#xff09;&#xff1a;基础知识 GPT&#xff08;Generative Pre-Trained Transformer&#xff09;&#xff1a;在不同任务中使用GPT GPT&#x…

单点登录设计方案介绍及用OAuth完整实现一个单点登录系统

文章目录 一、单点登录系统的意义1.1 提高用户体验1.2 提高安全性1.3 降低开发成本1.4 提高操作效率 二、单点登录的实现方式2.1 基于共享cookie的方式2.2 基于代理服务器的方式2.3 基于SAML协议的方式2.4 基于OAuth协议的方式 三、用OAuth实现单点登录3.1 OAuth 2.0 协议简介3…

【计组】微指令 微操作 微命令 微程序

区分四个概念 &#xff08;1&#xff09;微命令:微命令是构成控制信号序列的最小单位。通常是指那些直接作用于部件或控制门电路的控制命令。 &#xff08;2&#xff09;微操作:由微命令控制实现的最基本的操作称为微操作。 &#xff08;3&#xff09;微指令:以产生一组微命令&…

ubuntu20.4服务器安装mysql社区版并开放3306端口

您可以按照以下步骤在Ubuntu上安装MySQL社区版&#xff1a; 更新软件包列表&#xff1a; sudo apt-get update下载MySQL社区版的APT存储库配置文件&#xff1a; wget https://dev.mysql.com/get/mysql-apt-config_0.8.19-1_all.deb安装APT存储库配置文件&#xff1a; sudo …

Python基础篇(三):基本语句的示例和说明

基本语句的示例和说明 前言1. 赋值语句2. 条件语句3. 循环语句3.1 for循环示例3.2 while循环示例 4. 函数定义语句5. 函数调用语句6. 异常处理语句7. 导入语句 前言 本文是Python中常见基本语句的示例和说明。这些语句是构建Python程序逻辑和控制流的基础&#xff0c;可以根据…

【T+】畅捷通T+直接升级后,原端口无法使用提示端口占用。

【问题描述】 在使用畅捷通T软件过程中&#xff0c;由于软件可以直接升级&#xff0c; 即&#xff1a;原软件是T16.0版本&#xff0c;可以直接运行T18.0的程序进行软件、数据升级。无需将16.0进行卸载&#xff0c;再安装18.0进行数据升级。 但这样直接升级后&#xff0c;原来的…

Shell编程从入门到实践——入门篇

欢迎关注 「Android茶话会」 回 「学习之路」 取Android技术路线经典电子书回 「pdf」 取阿里&字节经典面试题、Android、算法、Java等系列武功秘籍。回 「天涯」 取天涯论坛200精彩博文,包括小说、玄学等 Shell是一种命令行解释器&#xff0c;它是在Unix和Linux操作系统中…

强化历程3-JavaWeb及Spring,SpringMVC,SpringBoot系列(2023.6.22)

文章目录 强化历程3-JavaWeb及SSM,SpringBoot系列(2023.6.22第一次更新)习题汇总1 JSP页面内容组成?2 cookie和session的区别?3 web应用中如何利用session来维持客户端和服务端的关系&#xff1f;4 session销毁的方式?5 请求转发和重定向的区别?6 什么是servelt&#xff1f…

基于Web的数字家庭网站设计与实现【附开题报告和万字文档(Lun文)】

主要功能 前台登录&#xff1a; ①主页&#xff1a;新闻信息展示、最新动态、家庭亲子视频展示、亲友动态展示 ②论坛&#xff1a;发布帖子 ③家庭亲自视频&#xff1a;视频类型分类、亲子视频标题 ④家庭日记&#xff1a;日记类型分类、日记标题 ⑤新闻信息&#xff1a;新闻类…