首页 > 其他分享 >golang 函数

golang 函数

时间:2023-02-21 11:24:35浏览次数:35  
标签:函数 int fmt golang func n1 n2

go语言中的函数特性

  • go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上 的函数)。
  • go语言中不允许函数重载(overload),也就是说不允许函数同名。
  • go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
  • 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
  • 函数可以作为参数传递给另一个函数。
  • 函数的返回值可以是一个函数。
  • 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
  • 函数参数可以没有名称。
  • 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。

函数的基本语法

func 函数名(形参列表)(返回值类型列表){
执行语句..
return + 返回值列表
}

  • 函数名:
    遵循标识符命名规范,驼峰命名,见名知意,首字母不能是数字。
    当首字母大写时,该函数可以被本包文件和其它包文件使用。
    当首学母小写只能被本包文件使用,其它包文件不能使用。
  • 形参列表:
    形参个数可以是一个参数,可以是n个参数,可以是0个参数。形参的作用为接收外来的数据。
  • 返回值类型列表:
    返回值个数为0:不用写返回值类型列表。
    返回值个数为1:括号内写一个返回类型,如(int),括号也可以不写直接写返回类型,如 int。
    返回值个数为多个:括号内写每个返回值的类型,以逗号隔开,如(int32,int8),接收也必须接收多个返回值,如果不需要接收可以_忽略。
package main

import "fmt"

func test(n1 int, n2 int) int { //定义自定义函数test,指定需要传入2个参数n1,n2(形参),参数类型为int,返回的值类型也为int
	var n3 int
	n3 = n1 + n2
	return n3 //定义返回n3
}

func main() {
	n4 := test(4, 5) //调用自定义函数test,传入第一个参数为4,第二个参数为5,(4,5为实参),定义n4接收函数test的返回值
	fmt.Printf("n4的值为:%v\n", n4)
	n5 := 11
	n6 := 50
	n7 := test(n5, n6)
	fmt.Printf("n7的值为:%v\n", n7) //将两个已经赋值的变量传入自定义函数test
}

执行结果

n4的值为:9
n7的值为:61

可变参数

使用...表示参数个数是可变数量的,函数内部处理可变参数的时候,将可变参数当做切片来处理。

package main

import "fmt"

func test2(num ...int) {
	for key, val := range num { //遍历可变参数
		fmt.Printf("num[%v]的值为:%v\n", key, val)
	}
}

func main() {
	fmt.Println("不传参数调用test2函数")
	test2()
	fmt.Println("传一个参数调用test2函数")
	test2(1)
	fmt.Println("传多个参数调用test2函数")
	test2(2, 3, 4, 5, 6)
}

执行结果

不传参数调用test2函数
传一个参数调用test2函数
num[0]的值为:1        
传多个参数调用test2函数
num[0]的值为:2        
num[1]的值为:3        
num[2]的值为:4        
num[3]的值为:5        
num[4]的值为:6 

函数的内存

当main函数中的n1,n2执行test1函数重新赋值时,只是在test1函数的内存空间中改变了n1,n2的值,跟main函数中的n1,n2的值无关,所以执行完test1函数时,n1,n2的值没有发生改变。

package main

import "fmt"

func test1(n1 int, n2 int) {
	n1 = 10 //重新给n1赋值
	n2 = 20 //重新给n2赋值
	fmt.Printf("test1函数中,n1的内存地址为:%p,n2的内存地址为%p\n", &n1, &n2)
}

func main() {
	var n1 int = 5
	var n2 int = 10
	fmt.Printf("n1的值为%v,n2的值为%v\n", n1, n2)
	fmt.Printf("n1的内存地址为:%p,n2的内存地址为%p\n", &n1, &n2)
	test1(n1, n2)
	fmt.Printf("执行test函数后,n1的值为%v,n2的值为%v\n", n1, n2)
}

执行结果

n1的值为5,n2的值为10
n1的内存地址为:0xc00001c0b8,n2的内存地址为0xc00001c0d0
test1函数中,n1的内存地址为:0xc00001c0d8,n2的内存地址为0xc00001c100
执行test函数后,n1的值为5,n2的值为10 

通过指针修改函数中变量的值。

main函数调用test1函数时,直接将变量的内存地址传入到test1函数,test1函数中,直接按照传入的地址指向的值进行修改,此时main函数中的n1,n2的地址还是之前的内存地址,在test1函数执行时已将内存地址指向的值修改了,所有重新输出的n1,n2的值为test1函数中的值。

package main

import "fmt"

func test1(n1 *int, n2 *int) {
	*n1 = 10 //将n1地址对应的值进行赋值
	*n2 = 20 //将n2地址对应的值进行赋值
	fmt.Printf("test1函数中,n1的值为:%p,n2的值为%p\n", n1, n2)
}

func main() {
	var n1 int = 5
	var n2 int = 10
	fmt.Printf("n1的值为%v,n2的值为%v\n", n1, n2)
	fmt.Printf("n1的内存地址为:%p,n2的内存地址为%p\n", &n1, &n2)
	test1(&n1, &n2) //将n1,n2的内存地址传入test1函数
	fmt.Printf("执行test函数后,n1的值为%v,n2的值为%v\n", n1, n2)
}

执行结果

n1的值为5,n2的值为10
n1的内存地址为:0xc00001c0b8,n2的内存地址为0xc00001c0d0
test1函数中,n1的值为:0xc00001c0b8,n2的值为0xc00001c0d0
执行test函数后,n1的值为10,n2的值为20  

函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。

package main

import "fmt"

func test(num1 int) {
	fmt.Println(num1)
}

func main() {
	f := test //将test函数赋值给f
	fmt.Printf("f的数据类型为:%T,test的数据类型为%T\n", f, test)
	f(8) //通过f调用test函数
}

执行结果

f的数据类型为:func(int),test的数据类型为func(int)
8

函数可以作为形参,并且调用

package main

import "fmt"

func test(num1 int) {
	fmt.Println(num1)
}

func test2(num2 int, fun func(int)) { //传入两个参数为,第一个为int类型,第二个为函数类型
	fmt.Printf("num2的值为%v,fun的值为%v\n", num2, fun)
}

func main() {
	f := test //将test函数赋值给f
	fmt.Printf("f的数据类型为:%T,test的数据类型为%T\n", f, test)
	test2(10, test) //直接传入函数名称
	test2(11, f)    //传入函数类型的变量
}

执行结果

f的数据类型为:func(int),test的数据类型为func(int)
num2的值为10,fun的值为0x60b120
num2的值为11,fun的值为0x60b120

type定义函数类型

type自定义数据类型

基本语法: type 自定义数据类型名(别名) 数据类型
例如:type aint int,则aint类型等价于int类型。
var num1 aint 等于 var num1 int

package main

import "fmt"

func main() {
	type aint int //将int类型取别名为aint
	var num1 aint = 10
	var num2 int = 20
	fmt.Printf("num1的值为:%v,num1的类型为:%T\n", num1, num1) //虽然是别名,但是在编译的时候还是认为不是一种数据类型
	sum := num2 + int(num1)                            //需要将num1类型aint转成int才能操作
	fmt.Printf("num1+num2的值为:%v", sum)
}

执行结果

num1的值为:10,num1的类型为:main.aint
num1+num2的值为:30

type自定义函数类型

基本语法: type 自定义函数名(别名) 函数类型
例如:type afunc func(int),则afunc类型等价于func(int)类型。

package main

import "fmt"

func test(num1 int) {
	fmt.Println(num1)
}

type afunc func(int) //定义函数别名

func test2(num2 int, fun afunc) { //传入两个参数为,第一个为int类型,第二个为函数类型
	fmt.Printf("num2的值为%v,fun的值为%v\n", num2, fun)
}

func main() {
	f := test //将test函数赋值给f
	fmt.Printf("f的数据类型为:%T,test的数据类型为%T,test2的数据类型为%T\n", f, test, test2)
	test2(10, test) //直接传入函数名称
	test2(11, f)    //传入函数类型的变量
}

执行结果

f的数据类型为:func(int),test的数据类型为func(int),test2的数据类型为func(int, mai
n.afunc)
num2的值为10,fun的值为0xb6b120
num2的值为11,fun的值为0xb6b120

对函数返回值命名

return返回值的类型和函数定义的返回值类型对应,顺序不能变

package main

import "fmt"

func test1(num1 int, str1 string) (int, string) { //返回的第一个值为int类型,第二个值的类型为string类型
	n1 := num1
	s1 := str1
	return n1, s1 //返回的第一个值必须也是int类型,第二个值也得是string类型
}

func main() {
	a, b := test1(10, "hello")
	fmt.Printf("a的值为:%v,b的值为:%v\n", a, b)
}

执行结果

a的值为:10,b的值为:hello

对函数返回值命名,return顺序随意,顺序不用对应函数返回值

package main

import "fmt"

func test1(num1 int, str1 string) (a int, b string) { //对函数的返回值命名了,所以return无需指定
	a = num1
	b = str1
	return 
}

func main() {
	a, b := test1(10, "hello")
	fmt.Printf("a的值为:%v,b的值为:%v\n", a, b)
}

执行结果

a的值为:10,b的值为:hello

init函数

每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用,也就是说 init 会在 main 函数前被调用。

注意事项与细节:
如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程全局变量定义->init 函数->main 函数

package main

import "fmt"

var n int = test10()

func test10() int {
	fmt.Println("执行test10函数")
	return 100
}

func main() {
	fmt.Println("执行main函数")
	fmt.Println(n)
}

func init() {
	fmt.Println("执行init函数")
}

执行结果

执行test10函数
执行init函数
执行main函数
100

注意:如果两个文件中都含有 变量定义,init 函数时,从引入的import开始,按照之前的顺序:变量定义 -> init函数 -> (进入main包)变量定义 -> init函数 -> main函数...

匿名函数

golang支持匿名函数,如果某些函数仅想在特定位置使用一次,那么可以考虑使用匿名函数,匿名函数也可以实现多次调用。

  • 可以在函数内部定义匿名函数,但是不能定义命名函数。
  • 匿名函数可以直接调用,保存到变量,作为参数,作为返回值。
  • 除了没有名字之外,匿名函数和命名函数没有什么不同。
  • 将局部变量赋值匿名函数,匿名函数可以在函数内部多次调用。
  • 将全局变量赋值匿名函数,匿名函数可以在程序中多次调用。
package main

import "fmt"

var res03 = func(n1 int, n2 int) int { //定义匿名函数,赋值给全局变量,调用这个全局变量等价于调用这个匿名函数
	return n1 + n2
}

func main() {
	res01 := func(n1 int, n2 int) int { //定义匿名函数,同时调用
		return n1 + n2
	}(10, 20) //将n1=10,n2=20传入匿名函数
	fmt.Printf("res01的值为:%v\n", res01)

	res02 := func(n1 int, n2 int) int { //定义匿名函数赋值给res02,res02等价于这个匿名函数
		return n1 + n2
	}
	sum1 := res02(30, 40) //调用匿名函数
	num1 := 100
	num2 := 140
	sum2 := res02(num1, num2) //调用匿名函数
	fmt.Printf("sum1的值为:%v\n", sum1)
	fmt.Printf("sum2的值为:%v\n", sum2)

	sum3 := res03(200, 210) //调用匿名函数
	fmt.Printf("sum3的值为:%v\n", sum3)
}

执行结果

res01的值为:30
sum1的值为:70 
sum2的值为:240
sum3的值为:410

闭包

闭包可以理解成定义在一个函数内部的函数,闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境
闭包的特点:

  • 返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数 ,因此这个匿名函数就和变量/参数形成一个整体,构成闭包。
  • 闭包中使用的变量/参数会一直保存在内存中,所以会一直使用---》意味着闭包不可滥用(对内存消耗大)
package main

import "fmt"

func get() func(int) int { //get函数返回一个函数类型,返回的函数类型的形参为int,返回的函数返回一个int类型
	var n1 int = 0
	a := func(n2 int) int {
		n1 += n2  //在匿名函数中调用get函数中的局部变量n1
		return n1 //匿名返回的返回,int类型
	}
	return a //get函数的返回,get函数里边嵌套一个匿名函数,返回里边的函数就形成了闭包。
}

func main() {
	f := get()         //将get函数赋值给f
	fmt.Println(f(10)) //f(n1)等于调用了get函数中的匿名函数,并给匿名函数传了n1,n1=10
	fmt.Println(f(20)) //因为变量n1常驻内存,每一次调用都会修改n1的值,使其加n2。n1=10+20=30
	fmt.Println(f(30)) //n1=30+30=60
}

执行结果
n1只在第一次调用初始化了(var n1 int = 0),后面的调用中n1都是上一次n1+n2的值。

10
30
60

标签:函数,int,fmt,golang,func,n1,n2
From: https://www.cnblogs.com/LI-XinYu/p/17134874.html

相关文章

  • Golang错误处理
    Golang中创建错误有两种方式:第一种:errors.New()函数,其返回值类型为*errors.errorString。第二种:fmt.Errorf()函数当使用fmt.Errorf()来创建错误时,核心有以下两......
  • 父节点递归查询和子节点递归查询函数
    本文为博主原创,未经允许不得转载:由于在项目中用到了向上查询父节点的递归查询和向下查询子节点查询的递归查询,由于在实现过程中,写递归查询的sql函数花费了较长的时间,所以在......
  • mysql中if()函数使用
    博主原创,转载请注明出处:     在mysql中if()函数的用法类似于java中的三目表达式,其用处也比较多,具体语法如下:IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的......
  • Python--字典底层存储、补充:类型对象、函数传参
    补充#python字典底层存储https://www.cnblogs.com/xiaoyuanqujing/articles/12008689.html#Python中数据类型都是对象都是地址引用,不存在值类型,都是......
  • T-SQL里数据库工程师都不知道的秘密之SQL Server自定义函数UDF
    T-SQLSQLServerUDF自定义函数概念与案例实战函数的定义这里的函数指的是用户自定义函数(UDF)全名为(user-definedfunction),以下简称为函数。它是数据库里的用户自定义程......
  • C++输出文件名、函数名、行号
    11std::cout<<"filepath=%s"<<__FILE__;//源文件名22std::cout<<"functionname=%s"<<__FUNCTION__;//函数名称33std::c......
  • 3 函数
    3.1def语句和参数和C语言一样,Python也可以在程序中自定义函数,写法如下:defhello(name):print('Hello,'+name)hello('taoA')hell0('World')3.2返回值和return语......
  • Python函数
    函数定义def函数名(参数列表):["注释块"](可选)语句块(代码块)return[返回值](可选)函数本身也是一个变量,该变量类型就是可调用类型,属性只读。参数列表和返......
  • 立即执行函数在前端国际化方案中的应用
    说起国际化,开发过跨区域网页的小伙伴应该都遇到过。我们的网页需要配置多套语言,方便用户进行切换。 本文就以React为例,介绍其中一种实现方案,并学习一下其中的知识点。......
  • 关于一维数组传入函数的使用 //西电oj214题字符统计
    #include<stdio.h>voidcount(charstr[],intnum[]){//形参用【】,传递数组首地址后可以直接正常用数组str[i] inti; for(i=0;str[i]!=0;i++){ if(str[i]>=65&&str[......