Go语言中的函数详解

1.函数的声明定义

//func关键字

//getStudent函数名

//(id int, classId int) 参数列表

//(name string,age int) 返回值列表

func getStudent(id int, classId int)(name string,age int) {

   //函数体

  if id==1&&classId==1{

     name = "BigOrange"

     age = 26

   }

  //返回值

  return name, age // 支持多重返回值

}

有意思的是Go语言的返回值可以有多个,并且放在了参数列表后面,而C#等都是在函数名之前,也没有关键字。

2.函数的调用

import "fmt"

//调用fmt包中的Println方法。

fmt.Println("Name:", std.Name, "Age:",std.Age)

3.函数编写的原则

很好奇为什么没有public private等关键字,那函数怎么才能定义为公用和私有呢?

Go语言有这样的规则:小写字母开头的函数只在本包内可见,大写字母开头的函数才

能被其他包使用。这个规则也适用于类型和变量的可见性。

4.不定参数问题

不定参数是指函数传入的参数个数为不定数量。

func myfunc(args ...int) {
     for _, arg := range args {
     fmt.Println(arg)
  }
}

函数myfunc()接受不定数量的参数,这些参数的类型全部是int

※形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。

它是一个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。

从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为

什么上面的参数args可以用for循环来获得每个传入的参数。

不定参数的传递

myfunc3(args ...int)

对应上面的这个函数,传递参数可以为下面两种

// 按原样传递
myfunc3(args...)
// 传递片段,实际上任意的int slice都可以传进去
myfunc3(args[1:]...)

任意类型的不定参数

可以看到 fmt.Println()方法接受了不定参数,但它是 ...interface{}

用interface{}传递任意类型数据是Go语言的惯例用法。

5.多返回值

Go语言函数可以返回多个返回值

如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单

地用一个下划线“_”来跳过这个返回值

6.匿名函数

Go语言支持随时在代码里定义匿名函数。

匿名函数由一个不带函数名的函数声明和函数体组成

func(a, b int, z float64) bool {
     return a*b <int(z)
}

匿名函数可以直接赋值给一个变量或者直接执行:(有点像js哈)

f := func(x, y int) int {
     return x + y
}
func(ch chan int) {
    ch <- ACK
} (reply_chan) // 花括号后直接跟参数列表表示函数调用

7.闭包

package main
import (
   "fmt"
)
func main() {
   var j int = 5
   a := func()(func()) {
      var i int = 10
      return func() {
         fmt.Printf("i, j: %d, %d\n", i, j)
      }
   }()
   a()
   j *= 2
   a()
}

结果:

i, j: 10, 5
i, j: 10, 10

分析:

1---"...func()(func()) {....."

表明此匿名函数返回值的类型是func(), 即此匿名函数返回一个函数指针(此处引用一下c 的概念);

2---"...returnfunc() {

fmt.Printf("i, j: %d, %d\n", i, j)

}..."

表明返回的函数指针指向一个打印i, j: %d, %d\n的函数;

3---"...a := func()(func()) {

...

}()..."

末尾的括号表明匿名函数被调用,并将返回的函数指针赋给变量a ;

综合来看:

"...a := func()(func()) {

var i int = 10

return func() {

fmt.Printf("i, j: %d, %d\n", i, j)

}

}()..."

此代码片段的意思"等价于"

a := func() {

fmt.Printf("i, j: %d, %d\n", i, j)

}

至于为何要用匿名函数如此的转一圈,是因为要引用闭包的概念,此概念省略不表,多写点代码试试就能体会了。

补充:传值和传引用

1.type 定义一个类型,有两种方式

  • ①配合struct,创建一个新的结构,类似C#里面的Class
  • ②配合既存的类型(int64...),创建一个新的类型,类似C++里面的typedef

2.Struct的如果不进行赋值,会使用0值进行初始化。

3.type使用既存类型定义新结构,和既存类型不是同一个类型,不能进行转换,例如

package main

type Integer int64

func main() {
    var srcInt Integer
    srcInt = int64(1000)
}

结果:

4.方法和函数的区别

方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字func 和方法名之间增加了一个参数

例如:

这是函数,它的调用就是直接在使用的时候 传入参数获取返回值就行

func getStudentName(student Student)(name string) {
    //返回值
    return student.Name
}

这是方法

package main

import (
    "fmt"
)

type Student struct
{
    Name string
    Age int
}

//Student类的方法 使用值接收者实现了一个方法
func (student Student) getStudentName()(string){
    return student.Name
}

//Student类的方法 使用指针接收者实现了一个方法
func (student *Student) changeStudentName(name string){
    student.Name = name
    fmt.Println("方法执行之后name",student.Name)

}

//Student类的方法 使用指针接收者实现了一个方法
func (student Student) changeStudentNameByValue(name string){
    student.Name = name
    fmt.Println("方法执行之后name",student.Name)
}

func main() {
    bigOrange:=Student{
        Name:"BigOrange",Age:18,
    }

    bigApple:=Student{
        Name:"BigApple",Age:20,
    }

    //使用函数获取学生名称
    name1 := getStudentName(bigOrange)
    name2 := getStudentName(bigApple)

    fmt.Println("========通过传地址ChangeName之后==========")
    fmt.Println("方法执行之前name",name1)
    bigOrange.changeStudentName("BigBanana")
    name1 = bigOrange.getStudentName()
    fmt.Println("方法返回之后Name",name1)

    fmt.Println("========通过传值ChangeName之后===========")
    fmt.Println("方法执行之前name",name2)
    bigApple.changeStudentNameByValue("BigPear")
    name2 = bigApple.getStudentName()
    fmt.Println("方法返回之后Name",name2)
}

结果:

========通过传地址ChangeName之后==========
方法执行之前name BigOrange
方法执行之后name BigBanana
方法返回之后Name BigBanana
========通过传值ChangeName之后===========
方法执行之前name BigApple
方法执行之后name BigPear
方法返回之后Name BigApple

上面的例子中

分别使用了函数和方法进行获取学生姓名

分别使用了传值和传地址的方式对学生名称进行修正

综上

  • ① Go语言中的方法,相比于函数,多了一个接收者参数
  • ② Go 语言里有两种类型的接收者:值接收者和指针接收者

方法如果使用 值接收者,那么调用者可以是值接收者类型、也可以是它的指针类型,请看下面的例子(补充上例):

fmt.Println("========使用指针来调用值类型声明的接收者方法===========")
bigGrape := &Student{ Name:"bigGrape",Age:22} //取地址!!!!
name3 := bigGrape.getStudentName();
fmt.Println("========通过传值ChangeName之后===========")
fmt.Println("方法执行之前name",name3)
bigGrape.changeStudentNameByValue("BigXXXX")
name3 = bigGrape.getStudentName()
fmt.Println("方法返回之后Name",name3)

结果:

========使用指针来调用值类型声明的接收者方法===========
name bigGrape
========通过传值ChangeName之后===========
方法执行之前name bigGrape
方法执行之后name BigXXXX
方法返回之后Name bigGrape

如上代码 使用了&获取地址,所以bigGrape是一个student类型的指针。下面的代码和上例一样,直接使用了【变量.方法】的方式调用方法,结果也和值类型调用的一样。

为什么?

【Go 在代码背后的执行动作】

name4:=(*bigGrape).getStudentName()

Go 编译器为了支持这种方法调用背后做的事情。【指针被解引用为值】,这样就符合了值接收者的要求。再强调一次,getStudentName 操作的是一个副本,只不过这次操作的是从bigGrape指针指向的值的副本。

同理还记得上面的,这句代码吗?对bigOrange这个变量修改了名称

bigOrange.changeStudentName("BigBanana")

bigOrange和bigApple明显是值类型,但是 changeStudentName 接收者是一个指针类型,为什么能调用,也是基于Go代码背后的执行动作,将值类型取了地址。

(&bigOrange).changeStudentName("BigOrange")

所以对于Go语言调用方法的类型,使用值或者指针都是可以的,不用拘泥于类型。

到此这篇关于Go语言函数的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Golang学习笔记(五):函数

    函数 Go语言里面的核心设计,通过关键字func来声明 复制代码 代码如下: func funcName(input type1, input2 type2) (output1 type1, output2 type2) {     //logical code     return value1, value2 } 基本语法 1.语法 复制代码 代码如下: //一般函数 func func_name(a int) {     println(a) } //多参数,无返回值 func func_

  • 深入解析golang编程中函数的用法

    函数是一组一起执行任务的语句.每Go程序具有至少一个函数,它一般是main(),以及所有的最琐碎程序可以定义附加函数. 你可以将代码放到独立的功能.如何划分代码之间的不同功能,但逻辑上的划分通常是让每个函数执行特定的任务. 函数声明告诉编译器有关的函数的名称,返回类型和参数.一个函数定义提供了函数的实际主体. Go语言标准库提供了大量的内置函数,在程序可以调用.例如,函数len()需要不同类型的参数和返回值的类型的长度.例如,如果一个字符串传递给它,它会返回字符串的长度以字节为单位,如果一个数组

  • Golang中的自定义函数详解

    不管是面向过程的编程,还是面向对象的编程,都离不开函数的概念,分别是,参数,函数名,返回值.接下来我们看看Go语言在这三个方面是做怎么操作的吧. 参数 谈到参数,我们在写函数或者是类中的方法的时候都需要考虑我们应该传递怎样的参数,或者是是否需要参数. 参数首先分为无参函数有参.无参也就是没有参数,也就不用写了. 有参 func functionTest() {  # 小括号内就是用来放参数的     # 函数体内 } Go语言是强数据类型的语言,参数是要指定类型的不然就报错.func 是函数的声

  • Golang教程之不可重入函数的实现方法

    函数function Go函数不支持嵌套.重载和默认参数 但支持以下特性: 无需声明原型 不定长度变参 多返回值 命名返回值参数 匿名函数 闭包 前言 一个不可重入的函数就是一个在任何时间点只能执行一次的函数,不管它被调用了多少次,以及有多少goroutines. 本篇文章说明了阻塞不可重入函数,并在golang中产生不可重入的函数实现. 场景用例 某个服务是对某些条件进行轮询,每秒监视一些状态.我们希望每个状态都可以独立地检查,而不需要阻塞.实现可能是这样的: func main() { ti

  • Golang字符串常用函数的使用

    目录 1)Golang字符串包含功能[区分大小写] 2)Golang ContainsAny()[区分大小写] 3)Golang Count() [区分大小写] 4)Golang EqualFold() [不区分大小写] 5) Golang Fields() 6)Golang FieldsFunc() 7)Golang HasPrefix() 8)Golang HasSuffix() 9)Golang Index() 10)Golang IndexAny() 11)Golang IndexByt

  • golang 函数返回chan类型的操作

    在阅读kafka的golang 客户端代码sarama-cluster时,遇到了如下一段代码: // Messages returns the read channel for the messages that are returned by // the broker. // // This channel will only return if Config.Group.Mode option is set to // ConsumerModeMultiplex (default). fu

  • Golang接口型函数使用小结

    目录 常规接口实现 接口型函数出场 进一步改造 什么是接口型函数?顾名思义接口函数指的是用函数实现接口,这样在调用的时候就会非常简便,这种方式适用于只有一个函数的接口. 这里以迭代一个map为例,演示这一实现的技巧. 常规接口实现 defer语句用于延迟函数调用,每次会把一个函数压入栈中,函数返回前再把延迟的函数取出并执行.延迟函数可以有参数: 延迟函数的参数在defer语句出现时就已确定下来(传值的就是当前值): 延迟函数执行按后进先出顺序执行: 延迟函数可操作主函数的具名返回值(修改返回值)

  • golang 函数以及函数和方法的详解及区别

    golang 函数以及函数和方法的区别 在接触到go之前,我认为函数和方法只是同一个东西的两个名字而已(在我熟悉的c/c++,python,java中没有明显的区别),但是在golang中者完全是两个不同的东西.官方的解释是,方法是包含了接收者的函数.到底什么意思呢. 首先函数的格式是固定的,func+函数名+ 参数 + 返回值(可选) + 函数体.例 func main() { fmt.Println("Hello go") } 在golang中有两个特殊的函数,main函数和ini

  • 秒懂Golang匿名函数

    上篇文章给大家介绍了go语言匿名函数的使用 需要的朋友点击查看.今天给大家介绍Golang匿名函数的相关知识,具体内容如下: 概念 所谓匿名函数,就是没有名字的函数 匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中.---wikipedia Golang是支持匿名函数的,即在需要使用函数时,再定义函数,匿名函数没有函数名,只有函数体,函数可以被作为一种类型被赋值给函数类型的变量,匿名函数往往以变量方式被传

  • Go语言中的函数详解

    1.函数的声明定义 //func关键字 //getStudent函数名 //(id int, classId int) 参数列表 //(name string,age int) 返回值列表 func getStudent(id int, classId int)(name string,age int) { //函数体 if id==1&&classId==1{ name = "BigOrange" age = 26 } //返回值 return name, age /

  • ASP 中 DateDiff 函数详解 主要实现两日期加减操作

    ASP 中 DateDiff 函数详解DateDiff 函数 描述  返回两个日期之间的时间间隔.  语法  DateDiff(interval, date1, date2 [,firstdayofweek][, firstweekofyear]])  DateDiff 函数的语法有以下参数: 参数 描述  interval 必选.字符串表达式,表示用于计算 date1 和 date2 之间的时间间隔.有关数值,请参阅"设置"部分.  date1, date2 必选.日期表达式.用于计

  • C/C++ 中memset() 函数详解及其作用介绍

    memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的: 包含在<string.h>头文件中,可以用它对一片内存空间逐字节进行初始化: 原型为 : void *memset(void *s, int v, size_t n); 这里s可以是数组名,也可以是指向某一内在空间的指针: v为要填充的值: n为要填充的字节数: 例子: struct data { char num[100]; char name[100]; int n; }; struct data a, b[10]; me

  • C语言中冒泡排序算法详解

    目录 一.算法描述 二.算法分析 三.完整代码 总结 一.算法描述 比较相邻两个元素,如果第一个比第二个大则交换两个值.遍历所有的元素,每一次都会将未排序序列中最大的元素放在后面.假设数组有 n 个元素,那么需要遍历 n - 1 次,因为剩下的一个元素一定是最小的,无需再遍历一次.因此需要两层循环,第一层是遍历次数,第二层是遍历未排序数组. 动图如下: 黄色部分表示已排好序的数组,蓝色部分表示未排序数组 核心代码如下: /** * @brief 冒泡排序 * * @param arr 待排序的数

  • C++ 中回调函数详解及简单实例

    C++ 中回调函数详解及简单实例 回调函数调用关系图 对于第一种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数. 对于第二种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数.主程序不知道宿主函数什么时候调用回调函数,因为调用函数启动了线程,这样,主程序就不必关心宿主函数什么时候调用回调函数了,主程序可以释放出来做自己的事了. 代码示例 #include <stdio.h> typedef void(*lpFunc

  • C语言中自定义类型详解

    目录 结构大小 offsetof 结构体对齐规则 存在原因 总结 结构大小 我们先随便给出一个结构体,为了计算他的大小,我给出完整的打印方案: typedef struct num { char c; int n; char cc; }num; int main() { printf("%d\n", sizeof(num)); return 0; } 好了,按道理来说我计算一个结构体大小就看他的各个成员需要消耗多大的空间, num 结构体中三个成员分别是 char ,int ,char

  • Python中reduce函数详解

    目录 1 reduce用法 2 reduce与for循环性能对比 reduce函数原本在python2中也是个内置函数,不过在python3中被移到functools模块中. reduce函数先从列表(或序列)中取出2个元素执行指定函数,并将输出结果与第3个元素传入函数,输出结果再与第4个元素传入函数,…,以此类推,直到列表每个元素都取完. 1 reduce用法 对列表元素求和,如果不用reduce,我们一般常用的方法是for循环: def sum_func(arr):     if len(a

  • Go语言中你不知道的Interface详解

    前言 最近在看Go语言的面向对象的知识点时,发现它的面向对象能力全靠 interface 撑着,而且它的 interface 还与我们以前知道的 interface 完全不同.故而整个过程不断的思考为什么要如此设计?这样设计给我们带来了什么影响? interface 我不懂你 Rob Pike 曾说: 如果只能选择一个Go语言的特 性移植到其他语言中,他会选择接口 被Go语言设计者如此看重,想来 interface 一定是资质不凡,颜值爆表.但是说实话,当我第一次读这部分内容的时候,我产生了以下

  • C++中inline函数详解

    本文主要记录了C++中的inline函数,也就是内联函数,主要记录了以下几个问题: 一.C++为什么引入inline函数? 主要目的:用它代替C语言中表达式形式的宏定义来解决程序中函数调用的效率问题. C语言中的宏定义:#define ExpressionName(var1,var2) (var1+var2)*(var1-var2)这种宏定义,它使用预处理器实现,没有了参数压栈.代码生成等一系列得到操作,因此效率很高.但缺点如下: 仅仅是做预处理器符号表中的简单替换,因此不能进行参数有效性的检测

  • C语言可变参数函数详解

    目录 C语言可变参数函数 总结 C语言可变参数函数 C 语言允许定义参数数量可变的函数,这称为可变参数函数(variadic function).这种函数需要固定数量的强制参数(mandatory argument),后面是数量可变的可选参数(optional argument). 这种函数必须至少有一个强制参数.可选参数的类型可以变化.可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定. C 语言中最常用的可变参数函数例子是 printf()和 scanf().这两个函数都

随机推荐