04-编织灵魂旋律:Golang 函数的魔力绽放

news2025/3/14 10:29:18

在这里插入图片描述

📃个人主页:个人主页
🔥系列专栏:Golang基础
💬Go(又称Golang)是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性,拥有高效的并发编程能力和简洁的语法。Go被设计用于构建可扩展、高性能的软件系统,具有优秀的内存管理和快速的编译速度,适用于Web开发、系统编程和云计算等领域。

文章目录

  • 函数的定义
  • 多个返回值
  • 变参
  • 传值与传指针
  • defer

函数的定义

函数 是Go里面的核心设计,它通过关键字 func 来声明,它的格式如下:

func f(input1 type1, input2 type2) (output1 type1, output2 type2) {
	//这里是处理逻辑代码
	//返回多个值
	return value1, value2
}

上面的代码可以看出:

  • 关键字 func 用来声明一个函数 f
  • 函数可以有一个或者多个参数,每个参数后面带有类型,通过 , 分隔
  • 函数可以返回多个值
  • 上面返回值声明了两个变量 output1 和 output2 ,如果不想声明也可以,直接就两个类型
  • 如果只有一个返回值且不声明返回值变量,那么可以省略 包括返回值的括号
  • 如果没有返回值,那么就直接省略最后的返回信息
  • 如果有返回值, 那么必须在函数的外层添加return语句

下面来看一个实际应用函数的例子(用来计算Max值):

package main
import "fmt"
// 返回a和b中最大值.
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func main() {
	x := 3
	y := 4
	z := 5
	max_xy := max(x, y) //调用函数max(x, y)
	max_xz := max(x, z) //调用函数max(x, z)
	fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
	fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
	fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
}

上面这个里面可以看到 max 函数有两个参数,它们的类型都是 int ,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int),默认为离它最近的类型,同理多于2个同类型的变量或者返回值。同时注意到它的返回值就是一个类型,这个就是省略写法。

多个返回值

Go语言比C更先进的特性,其中一点就是函数能够返回多个值。
直接看例子:

package main
import "fmt"
//返回 A+B 和 A*B
func f(A, B int) (int, int) {
	return A+B, A*B
}
func main() {
	x := 3
	y := 4
	res1, res2 := f(x, y)
	fmt.Printf("%d + %d = %d\n", x, y, res1)
	fmt.Printf("%d * %d = %d\n", x, y, res2)
}

上面的例子可以看到直接返回了两个参数,当然也可以命名返回参数的变量,这个例子里面只是用了两个类型,也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差。

func SumAndProduct(A, B int) (res1 int, res2 int) {
	res1 = A+B
	res2 = A*B
	return
}

变参

Go函数支持变参。
接受变参的函数是有着不定数量的参数的。
为了做到这点,首先需要定义函数使其接受变参:

func f(arg ...int) {}

arg …int 告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是 int 。在函数体中,
变量 arg 是一个 intslice

for _, n := range arg {
	fmt.Printf("%d\n", n)
}

传值与传指针

传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。
为了验证上面的说法,来看一个例子:

package main
import "fmt"
//简单的一个函数,实现了参数+1的操作
func add1(a int) int {
	a = a+1 // 改变了a的值
	return a //返回一个新值
}
func main() {
	x := 3
	fmt.Println("x = ", x) // 应该输出 "x = 3"
	x1 := add1(x) //调用add1(x)
	fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4"
	fmt.Println("x = ", x) // 应该输出"x = 3"
}

虽然调用了 add1 函数,并且在 add1 中执行 a = a+1 操作,但是上面例子中 x 变量的值没有发生变化。
理由很简单:因为当调用 add1 的时候, add1 接收的参数其实是 x 的copy,而不是 x 本身。
如果真的需要传这个 x 本身,该怎么办呢?
这就牵扯到了所谓的指针。
变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。
只有 add1 函数知道 x 变量所在的地址,才能修改 x 变量的值。
所以需要将 x 所在地址 &x 传入函数,并将函数的参数的类型由 int 改为 *int ,即改为指针类型,才能在函数中修改 x 变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。
请看下面的例子:

package main
import "fmt"
//简单的一个函数,实现了参数+1的操作
func add1(a *int) int { // 请注意,
	*a = *a+1 // 修改了a的值
	return *a // 返回新值
}
func main() {
	x := 3
	fmt.Println("x = ", x) // 应该输出 "x = 3"
	x1 := add1(&x) // 调用 add1(&x) 传x的地址
	fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4"
	fmt.Println("x = ", x) // 应该输出 "x = 4"
}

这样,就达到了修改 x 的目的。
那么到底传指针有什么好处呢?
传指针使得多个函数能操作同一个对象。
传指针比较轻量级 (8bytes),只是传内存地址,可以用指针传递体积大的结构体。
如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当要传递大的结构体的时候,用指针是一个明智的选择。
Go语言中 channelslicemap 这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变 slice 的长度,则仍需要取地址传递指针)

defer

Go语言中有种不错的设计,即延迟(defer)语句,可以在函数中添加多个 defer 语句。
当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。
特别是当进行一些打开资源的操作时,遇到错误需要提前返回,在返回前需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,一般写打开一个资源是这样操作的:

func ReadWrite() bool {
	file.Open("file")
	// 做一些工作
	if failureX {
		file.Close()
		return false
	}
	if failureY {
		file.Close()
		return false
	}
	file.Close()
	return true
}

上面有很多重复的代码,Go的 defer 有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。
在 defer 后指定的函数会在函数退出前调用。

func ReadWrite() bool {
	file.Open("file")
	defer file.Close()
	if failureX {
		return false
	}
	if failureY {
		return false
	}
	return true
}

如果有很多调用 defer ,那么 defer 是采用后进先出模式,所以如下代码会输出:

 4 3 2 1 0
for i := 0; i < 5; i++ {
   defer fmt.Printf("%d ", i)
}

通常来说,defer 会用在释放数据库连接,关闭文件等需要在函数结束时处理的操作。

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

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

相关文章

常见网络设备及其功能

1.常见网络设备及其功能 笔记来源&#xff1a; Computer network components and their functions 设备工作所在层隔离冲突域隔离广播域路由器(Router)网络层√√网桥(Bridge)数据链路层√交换机(Switch)数据链路层√中继器(Repeater)物理层集线器(Hub)物理层 1.1 路由器&…

chatgpt赋能python:Python并排输出数字——快速高效的实现技巧

Python并排输出数字——快速高效的实现技巧 在Python编程中&#xff0c;我们经常需要对数字进行输出&#xff0c;并排输出数字是一种非常常见的需求。比如说&#xff0c;我们需要将多个数据进行比较&#xff0c;或者需要将多个相关数据进行显示&#xff0c;等等。本文将介绍Py…

一.基于压缩感知(CS)的DOA估计方法-OMP-CS算法

阅读须知&#xff1a; 1.本文为本人原创作品仅供学习参考&#xff0c;未经过本人同意禁止转载和抄袭。 2.要想无障碍阅读本文需要一定的压缩感知理论以及压缩感知信号重构算法基础。 3.话不多说&#xff0c;直接开搞。 1 基于压缩感知DOA估计方法原理 假设有K个远场窄带信号…

dpdk21.11 编译(meson+ninja)及VFIO模块的加载和运行

目录 前言 安装前的环境配置 编译流程 1. 设置环境变量&#xff08;好像也不需要了&#xff09; 2. 构建dpdk 3. 编译 执行测试 1. 绑定vfio-pci 模块 2. 挂载网卡 3. 设置大页 4. 启动测试程序-testpmd 前言 操作系统&#xff1a;ubuntu22.04.2 LTS 内核版本&#…

深入理解 Java ServiceLoader、Dubbo ExtensionLoader 源码结合实战篇

介绍Java SPIDriver 实现类DriverManager 驱动管理器类loadInitialDrivers 方法registerDriver 方法getConnection 方法 ServiceLoader 核心类LazyIterator#hasNextService 方法LazyIterator#nextService 方法 Dubbo SPI加载策略FilterExtensionLoaderExtensionLoader#getExten…

框架篇面试详解

spring AOP AOP称为面向切面编程&#xff0c;用于将那些与业务无关&#xff0c;但却对多个对象产生影响的公共行为和逻辑&#xff0c;抽取并封装成为一个可重用的模块&#xff0c;这个模块被命名为“切面”&#xff08;Aspect&#xff09;&#xff0c;减少系统中的重复代码&am…

02-舞动数据类型:Golang 类型定义的奇妙之旅

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Golang基础 &#x1f4ac;Go&#xff08;又称Golang&#xff09;是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性&#xff0c;拥有高效的并发编程能力和简洁的语法。G…

chatgpt赋能python:Python如何建设网页并实现客户端与服务器端的通信

Python如何建设网页并实现客户端与服务器端的通信 介绍 Python是一种流行的编程语言&#xff0c;被广泛应用于Web开发。在Web开发中&#xff0c;Python可以用来建立并联接网页与服务器端的通信。 这篇文章将介绍如何使用Python建立网页&#xff0c;并展示如何实现客户端与服…

Science Advance||个体脑中鲁棒的动态脑网络

文章目录 个体化动态方法&#xff08;INSCAPE 方法&#xff09;&#xff1a;&#xff08;A&#xff09;生成脑共同激活状态的组模板&#xff1a;&#xff08;B&#xff09;个体水平分析&#xff1a; 不同的大脑状态有特定的协同激活模式&#xff08;coactivation patterns&…

IntSet

基本概述 IntSet是Redis中set集合的一种实现方式&#xff0c;基于整数数组来实现&#xff0c;并且具备长度可变、有序等特征。 结构如下&#xff1a; typedef struct intset {uint32_t encoding; /* 编码方式&#xff0c;支持存放16位、32位、64位整数&#xff08;4字节32比特…

chatgpt赋能python:Python如何并排输入数字

Python如何并排输入数字 Python是一个功能强大的编程语言&#xff0c;可以用于各种用途&#xff0c;包括数据分析、机器学习、Web开发等。对于很多初学者来说&#xff0c;学会如何并排输入数字可能是一个基础的技巧。在本文中&#xff0c;我们将介绍如何使用Python在同一行中输…

RK3588平台开发系列讲解(USB篇)USB 常用调试方法

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、USB枚举成功标志二、USB speed查询三、USB 查询PID、VID四、USB 当前 端口组合五、USB 动态打印debug日志六、USB IPC log分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要 介绍 USB 常用调试…

安卓期末考试知识总结(2)

文章目录 第四章&#xff1a;程序活动单元Activity四大组件Activity生命周期Activity的启动模式standardsingleTopsingleInstancesingleTask IntentIntentFilterActivity之间的数据传递putExtra()方法传递数据Bundle类传递数据Activity之间的数据回传 练习总结 第四章&#xff…

Java虚拟机原理

Java是一种跨平台的语言&#xff0c;这意味着Java开发出来的程序经过编译后&#xff0c;可以在Linux上运行&#xff0c;也可以在Windows上运行&#xff1b;可以在PC、服务器上运行&#xff0c;也可以在手机上运行&#xff1b;可以在X86的CPU上运行&#xff0c;也可以在ARM的CPU…

chatgpt赋能python:Python引入Math库的使用方法

Python引入Math库的使用方法 Python作为一门强大的编程语言&#xff0c;有着广泛的应用场景。在计算领域中&#xff0c;Python也有很多优秀的库来进行相应的计算。其中一个广泛使用的库就是Math库。这个库包含了很多数学函数&#xff0c;如三角函数、幂函数、对数函数等等。在…

剑指offer21.调整数组顺序使奇数位于偶数前面

毫无难度。 class Solution {public int[] exchange(int[] nums) {int i 0;int size nums.length;int[] res new int[size];int jsize-1;for(int k 0; k<size;k){if(nums[k]%2 0){res[j]nums[k];j--;}else{res[i]nums[k];i;}}return res;} }

关于 Ceph 存储集群配置的一些笔记

写在前面 Ceph 考试整理笔记&#xff0c;老师总结基础上&#xff0c;略有补充博文内容涉及&#xff1a; ceph 集群的配置简单介绍永久和零时修改集群配置文件集群 Mon 的配置集群身份验证的配置集群多网络的配置 理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有…

Mysql 索引调优

前言 索引是帮助MySQL高效获取数据的数据结构 常用索引 1、普通索引 普通索引是最基本的索引&#xff0c;仅用于加速查询&#xff0c;没有任何限制&#xff1a;可以为空、可以重复 2、唯一索引 唯一索引与普通索引类似&#xff0c;但索引列的值必须唯一 3、主键索引 主…

chatgpt赋能python:Python怎么将界面和程序交互

Python怎么将界面和程序交互 随着互联网技术的不断发展和普及&#xff0c;越来越多的人开始关注于网站的设计和开发。在Web应用程序的开发过程中&#xff0c;与用户进行交互是至关重要的一个方面&#xff0c;而Python作为一种强大的开发语言&#xff0c;可以很好地帮助我们实现…

C++中防止头文件重复包含处理办法

首先给出可以拷贝的模板&#xff1a; #ifndef _ADDNUM_H_ #define _ADDNUM_H_这里加上相应的函数声明即可 #endif在小型项目中&#xff0c;如果将函数的定义写在main函数的后面&#xff0c;那么需要在main函数前面加上这个函数的声明才可以顺利运行成功。 #include <iostr…