十二、go语言函数的初步介绍

一、函数

1.1 什么是函数

函数是执行特定任务的代码块。

1.2 函数的声明

go语言至少有一个main函数,在之前的章节示例中一直在使用

语法格式:

func funcName(parametername type1, parametername type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}
  • func:函数由 func 开始声明
  • funcName:函数名称,函数名和参数列表一起构成了函数签名。
  • parametername type:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • output1 type1, output2 type2:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
  • 上面返回值声明了两个变量output1和output2,如果你不想声明也可以,直接就两个类型。
  • 如果只有一个返回值且不声明返回值变量,那么你可以省略包括返回值的括号(即一个返回值可以不声明返回类型)
  • 函数体:函数定义的代码集合。

1.3 函数的使用

示例代码:

package main

import (
	"fmt"
	"strconv"
)

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   var ret int

   /* 调用函数并返回最大值 */
   ret = max(a, b)
   fmt.Printf( "最大值是 : %d\n", ret )
   /* 声明函数变量 */
   max2 := func(num1, num2 int64) string{
		var result int64
		if num1 > num2 {
			result = num1
		} else {
			result = num2
		}
       //回顾下字符串转换
		return strconv.FormatInt(result, 10)
	}
    fmt.Printf( "最大值是 : %v\n", max2(300, 400))
}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 定义局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result 
}

运行结果:

最大值是 : 200
最大值是 : 400

1.4 函数参数的使用

形式参数:定义函数时,用于接收外部传入的数据,叫做形式参数,简称形参。

实际参数:调用函数时,传给形参的实际的数据,叫做实际参数,简称实参。

函数调用:

​ A:函数名称必须匹配

​ B:实参与形参必须一一对应:顺序,个数,类型

1.5 函数可变参

  • Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参:
func myfunc(arg ...int) {}
  • 语法:参数名 … 参数的类型
  • 对于函数,可变阐述相当于一个切片;调用函数的时候,可以传入0-多个 参数
  • 注意事项:
    • 如果一个函数的参数是可变参数,同时还有其他的参数,可变参数要放在参数列表的最后
    • 一个函数的参数列表中最多只能有一个可变参数

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

package main

import (
	"fmt"
)

func main() {
	myfunc()
	myfunc1(1, 2,3,4,5)

}
func myfunc(arg ...int) {fmt.Println(arg)}

func myfunc1(arg ...int) {
	for _, n := range arg {
		fmt.Printf("And the number is: %d\n", n)
	}
}

func myfunc2(arg, arg2 string, arg3 ...int) {fmt.Println(arg)}
//输出
[]
And the number is: 1
And the number is: 2
And the number is: 3
And the number is: 4
And the number is: 5

1.3 参数传递

  • go语言函数的参数也是存在值传递引用传递

  • 值传递

    package main
    
    import (
       "fmt"
       "math"
    )
    
    func main(){
       /* 声明函数变量 */
       getSquareRoot := func(x float64) float64 {
          return math.Sqrt(x)
       }
    
       /* 使用函数 */
       fmt.Println(getSquareRoot(9))
    
    }
    
  • 引用传递

    • 这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有add1函数知道x变量所在的地址,才能修改x变量的值。所以我们需要将x所在地址&x传入函数,并将函数的参数的类型由int改为*int,即改为指针类型,才能在函数中修改x变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子(建议自己复制代码运行一下,更清晰明了)。

    • 看例子前说一些注意事项

      • 在 Go 语言中,[4]int[]int 不是同一类型
      • [4]int 是一个包含 4 个整数的数组,而 []int 是一个包含任意数量整数的切片(slice)。
      • 数组是一种固定长度的数据结构,而切片是一种可变长度的数据结构。因此,[4]int 数组的长度是固定的,而 []int 切片的长度可以在运行时动态变化。
      • 数组和切片在内存中的存储方式也不同。数组是连续的内存块,而切片则是包含指向底层数组的指针、长度和容量信息的结构体
      • 虽然它们都表示整数序列,但它们是不同的数据类型,不能直接进行类型转换或赋值操作
      package main
      
      import "fmt"
      
      func main() {
      	arr := [4]int{1,2,3,4}
      	fmt.Printf("arr的内存地址: %T %p \n", arr, arr)
      	arr2 := []int{1,2,3,4}
      	fmt.Printf("函数中结束:arr2的内存地址: %T %p \n", arr2, arr2)
      	fun1(arr)
      	fmt.Println(arr)
      	fmt.Println("----------------------")
      	fun2(arr2)
      	fmt.Println(arr2)
      }
      
      func fun1 (arr [4]int){
      	fmt.Printf("函数中起始:arr的内存地址: %p \n", arr)
      	fmt.Println("函数中,数组的数据:", arr)
      	arr[0] = 100
      	fmt.Println("函数中,数组的数值修改后:", arr)
      	fmt.Printf("函数中结束:arr的内存地址: %p \n", arr)
      }
      func fun2 (arr2 []int){
      	fmt.Printf("函数中起始:arr的内存地址: %p \n", arr2)
      	fmt.Println("函数中,数组的数据:", arr2)
      	arr2[0] = 100
      	fmt.Println("函数中,数组的数值修改后:", arr2)
      	fmt.Printf("函数中结束:arr的内存地址: %p \n", arr2)
      }
      //输出
      arr的内存地址: [4]int %!p([4]int=[1 2 3 4]) //打印地址报错
      函数中结束:arr2的内存地址: []int 0xc0000d0060 //地址是不变的
      函数中起始:arr的内存地址: %!p([4]int=[1 2 3 4]) 
      函数中,数组的数据: [1 2 3 4]
      函数中,数组的数值修改后: [100 2 3 4]
      函数中结束:arr的内存地址: %!p([4]int=[100 2 3 4]) //打印地址报错
      [1 2 3 4]
      ----------------------
      函数中起始:arr的内存地址: 0xc0000d0060 //地址是不变的
      函数中,数组的数据: [1 2 3 4]
      函数中,数组的数值修改后: [100 2 3 4]
      函数中结束:arr的内存地址: 0xc0000d0060 //地址是不变的
      [100 2 3 4] //同一地址,所以切片中值改变
      
    • 传指针使得多个函数能操作同一个对象。

    • 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。

    • Go语言中slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)

你可能感兴趣的:(go语言学习,golang,开发语言,后端)