[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. Docker:docker部署PXC-5.7.21(mysql5.7.21)集群搭建负载均衡实现双机热部署方案

    单节点数据库弊端 大型互联网程序用户群体庞大,所以架构必须要特殊设计 单节点的数据库无法满足性能上的要求 单节点的数据库没有冗余设计,无法满足高可用 推荐Mysql集群部署方案 PXC (Percon ...

  2. Parrot os 安装vmtools

    1.更新源(这步个人觉得官方源还可以,没网上说的那么慢) vim /etc/apt/sources.list.d/parrot.list linux命令 ,按i进入修改模式,修改结束,之后先按esc, ...

  3. bugku--cookie欺骗

    打开题目一看,是一串的东西,再看了一下filename发现不对劲了,明显是base64编码,拿去解码一下, 发现是这个,说明是filename,是需要解析的哪个文件名,把index.php编码一下,试 ...

  4. 从零开始给女朋友讲计算机 1 - 从比特、字节、补码到 ASCII、GB2312、Unicode

    起因 在代码 review 的过程中,总是发现有人在数据类型转换(reinterpret_cast).大小端的问题(什么情况下需要考虑大小端,什么情况下不需要考虑)上犯错误,究其原因是没有彻彻底底地搞 ...

  5. Java | 日期类型的绍介和操作

    Date类 Date类在java.util.Date,Date类表示特定的瞬间,精确到毫秒.(毫秒是千分之一秒)毫秒可以对时间和日期进行计算,可以把日期转换为毫秒进行计算,计算完毕,再把毫秒转换为日期 ...

  6. "Shortest" pair of paths[题解]

    "Shortest" pair of paths 题目大意 给出 \(n\) 个点,\(m\) 条边,除第一个点和最后一个点外,其他所有的点都只能被经过一次,要求找到两条从第一个点 ...

  7. P4480 「BJWC2018」「网络流与线性规划24题」餐巾计划问题

    刷了n次用了奇淫技巧才拿到rk1,亥 这道题是网络流二十四题中「餐巾计划问题」的加强版. 于是怀着试一试的心情用费用流交了一发: 哇塞,过了9个点!(强烈谴责出题人用*造数据 下面是费用流解法简述: ...

  8. jconsole和jstack

    1.jconsole jconsole是jdk自带的一个可视化的jvm监控工具,可以监控本地的jvm,也可以监控远程jvm 打开本地jdk安装目录下的bin目录下的jconsole.exe 2.jst ...

  9. Qt Model/view 小实例 文件目录浏览器

    1. 文件目录浏览器 直接在main.cpp文件中添加下列代码 #include "mainwindow.h" #include <QApplication> #inc ...

  10. html自定义加载动画

    整体代码 HTML 实现自定义加载动画,话不多说如下代码所示: <!DOCTYPE html> <html lang="en"> <head> ...