[11 Go语言基础-可变参数函数]

可变参数函数

什么是可变参数函数

可变参数函数是一种参数个数可变的函数。

语法

如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最后一个参数。

请注意只有函数的最后一个参数才允许是可变的。

通过一些例子理解可变参数函数如何工作

你是否曾经想过 append 函数是如何将任意个参数值加入到切片中的。这样 append 函数可以接受不同数量的参数。

func append(slice []Type, elems ...Type) []Type

上面是 append 函数的定义。在定义中 elems 是可变参数。这样 append 函数可以接受可变化的参数。

让我们创建一个我们自己的可变参数函数。我们将写一段简单的程序,在输入的整数列表里查找某个整数是否存在。

package main

import (
"fmt"
) func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)
find(45, 56, 67, 45, 90, 109)
find(78, 38, 56, 98)
find(87)
}

在上面程序中 func find(num int, nums ...int) 中的 nums 可接受任意数量的参数。在 find 函数中,参数 nums 相当于一个整型切片。

可变参数函数的工作原理是把可变参数转换为一个新的切片。以上面程序中的第 22 行为例,find 函数中的可变参数是 89,90,95 。 find 函数接受一个 int 类型的可变参数。因此这三个参数被编译器转换为一个 int 类型切片 int []int{89, 90, 95} 然后被传入 find函数。

在第 10 行, for 循环遍历 nums 切片,如果 num 在切片中,则打印 num 的位置。如果 num 不在切片中,则打印提示未找到该数字。

上面代码的输出值如下,

type of nums is []int
89 found at index 0 in [89 90 95] type of nums is []int
45 found at index 2 in [56 67 45 90 109] type of nums is []int
78 not found in [38 56 98] type of nums is []int
87 not found in []

在上面程序的第 25 行,find 函数仅有一个参数。我们没有给可变参数 nums ...int 传入任何参数。这也是合法的,在这种情况下 nums 是一个长度和容量为 0 的 nil 切片。

给可变参数函数传入切片

下面例子中,我们给可变参数函数传入一个切片,看看会发生什么。

package main

import (
"fmt"
) func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums)
}

在第 23 行中,我们将一个切片传给一个可变参数函数。

这种情况下无法通过编译,编译器报出错误 main.go:23: cannot use nums (type []int) as type int in argument to find

为什么无法工作呢?原因很直接,find 函数的说明如下,

func find(num int, nums ...int)

由可变参数函数的定义可知,nums ...int 意味它可以接受 int 类型的可变参数。

在上面程序的第 23 行,nums 作为可变参数传入 find 函数。前面我们知道,这些可变参数参数会被转换为 int 类型切片然后在传入 find 函数中。但是在这里 nums 已经是一个 int 类型切片,编译器试图在 nums 基础上再创建一个切片,像下面这样

find(89, []int{nums})

这里之所以会失败是因为 nums 是一个 []int类型 而不是 int类型。

那么有没有办法给可变参数函数传入切片参数呢?答案是肯定的。

有一个可以直接将切片传入可变参数函数的语法糖,你可以在在切片后加上 ... 后缀。如果这样做,切片将直接传入函数,不再创建新的切片

在上面的程序中,如果你将第 23 行的 find(89, nums) 替换为 find(89, nums...) ,程序将成功编译并有如下输出

type of nums is []int
89 found at index 0 in [89 90 95]

下面是完整的程序供您参考。

package main

import (
"fmt"
) func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums...)
}

不直观的错误

当你修改可变参数函数中的切片时,请确保你知道你正在做什么。

下面让我们来看一个简单的例子。

package main

import (
"fmt"
) func change(s ...string) {
s[0] = "Go"
} func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}

你认为这段代码将输出什么呢?如果你认为它输出 [Go world] 。恭喜你!你已经理解了可变参数函数和切片。如果你猜错了,那也不要紧,让我来解释下为什么会有这样的输出。

在第 13 行,我们使用了语法糖 ... 并且将切片作为可变参数传入 change 函数。

正如前面我们所讨论的,如果使用了 ...welcome 切片本身会作为参数直接传入,不需要再创建一个新的切片。这样参数 welcome 将作为参数传入 change 函数

change 函数中,切片的第一个元素被替换成 Go,这样程序产生了下面的输出值

[Go world]

这里还有一个例子来理解可变参数函数。

package main

import (
"fmt"
) func change(s ...string) {
s[0] = "Go"
s = append(s, "playground")
fmt.Println(s)
} func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}


可变长参数(小练习)

package main

import "fmt"

// 可变长参数

func main() {
//test2("11111","2")
var a []string=[]string{"lqz","egon"}
test2(a...) // 把切片传入 } func test2(a ...string) { // a是切片
fmt.Println(a)
fmt.Println(a[1])
fmt.Printf("%T",a)
a=append(a,"lqz")
fmt.Println(a)
}

[11 Go语言基础-可变参数函数]的更多相关文章

  1. C语言中可变参数函数实现原理

    C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...

  2. C语言可变参数函数实现原理

    一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本 ...

  3. c语言可变参数函数

    c语言支持可变参数函数.这里的可变指,函数的参数个数可变. 其原理是,一般情况下,函数参数传递时,其压栈顺序是从右向左,栈在虚拟内存中的增长方向是从上往下.所以,对于一个函数调用 func(int a ...

  4. C语言可变参数函数的编写

    1. 引言 C语言我们接触的第一个库函数是 printf(“hello,world!”);其参数个数为1个. 然后,我们会接触到诸如: printf(“a=%d,b=%s,c=%c”,a,b,c);此 ...

  5. C语言中可变参数的函数(三个点,“...”)

    C语言中可变参数的函数(三个点,“...”) 本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end ...

  6. C语言中的可变参数函数

    C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个参数format固定以外 ...

  7. C语言学习020:可变参数函数

    顾名思义,可变参数函数就是参数数量可变的函数,即函数的参数数量是不确定的,比如方法getnumbertotal()我们即可以传递一个参数,也可以传递5个.6个参数 #include <stdio ...

  8. C语言可变参数函数详解示例

    先看代码 printf(“hello,world!”);其参数个数为1个. printf(“a=%d,b=%s,c=%c”,a,b,c);其参数个数为4个. 如何编写可变参数函数呢?我们首先来看看pr ...

  9. php匿名函数和可变参数函数

    php匿名函数和可变参数函数 简介 直接上代码了: <?php $test1 = function($value) { echo $value; }; $test1('HelloWorld'); ...

随机推荐

  1. 查看python的安装版本,位数及安装路径

    一.想要查看ubuntu中安装的Python路径 方法一:whereis python (用来快速查找任何文件,是一个文件搜索命令,与locate的功能一样.执行whereis python 会将所有 ...

  2. 可执行jar包在windows server2008下的自启动

    最近要部署项目的服务端在windows server2008下面,所以把项目打包成可执行的jar包,然后希望它能开机自启动,毕竟每次都在cmd下输入java -jar xxx.jar才能启动太繁琐了. ...

  3. 经典论文系列 | 目标检测--CornerNet & 又名 anchor boxes的缺陷

    ​ 前言: 目标检测的预测框经过了滑动窗口.selective search.RPN.anchor based等一系列生成方法的发展,到18年开始,开始流行anchor free系列,CornerNe ...

  4. 使用Hugo框架搭建博客的过程 - 主题配置

    前言 博客部署完成后,恭喜你可以发表第一篇:Hello world!但是LoveIt这么好用的主题,不配置一番可惜了. 基本功能配置 主题配置最好参考已有的配置,比如LoveIt作者写的介绍,还有主题 ...

  5. SqlServer常用语句整理

    先记录下来 以后整理 1.常用语句 1.1update连表更新 update a set a.YCaseNo = a.WordName + '['+ convert(varchar,a.CaseYea ...

  6. [刘阳Java]_Spring入门_第1讲

    Spring框架在企业中的使用非常多,优势明显.所以学好Spring框架肯定不言而喻.今天我们给大家介绍Spring的入门 1. 对于初学者来说我们要学习Spring框架中的哪些技术,这个有必要了解一 ...

  7. uiautomator2 入门教程

    一.前言 在 Android 自动化测试方面,Google 提供了一个基于 Java 开发的库 UiAutomator,基本上支持所有的 Android 事件操作,使用简单. 在此基础上,有大佬开发出 ...

  8. CSS设置height为100%无效的情况

    CSS设置height为100%无效的情况 笔者是小白,不是特别懂前端.今天写一个静态的HTML页面,然后想要一个div占据页面的100%,但是尝试了很多办法都没有实现,不知道什么原因. 后来取百度搜 ...

  9. 每天五分钟Go - 闭包

    闭包的示例代码 func getSequence() func() int{ i:=0 return func() int { i+=1 return i } } 首先,函数名getSequence, ...

  10. Socket介绍(五)

    套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接 ...