返回顶部
首页 > 资讯 > 精选 >Go语言Slice作为函数参数的使用方法
  • 946
分享到

Go语言Slice作为函数参数的使用方法

2023-06-20 13:06:30 946人浏览 泡泡鱼
摘要

这篇文章主要讲解了“Go语言Slice作为函数参数的使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言Slice作为函数参数的使用方法”吧!目录前言问题与解析典型问题其它疑问1其

这篇文章主要讲解了“Go语言Slice作为函数参数的使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言Slice作为函数参数的使用方法”吧!

目录
  • 前言

  • 问题与解析

    • 典型问题

  • 其它疑问1

    • 其它疑问2

      • 结论

        • 参考链接

          前言

          首先要明确Go语言中实质只有值传递,引用传递和指针传递是相对于参数类型来说。

          个人认为上诉的结论不对,把引用类型看做对指针的封装,一般封装为结构体,结构体是值类型,所以感觉都是值传递。不然我感觉其它语言实质不也都是值传递?不过我刚学Go,可能还没完全弄懂,这个有问题可以互相讨论下。

          Go语言中的值类型:int、float、bool、array、sturct等,声明一个值类型变量时,编译器会在栈中分配一个空间,空间里存储的就是该变量的值。

          Go语言中的引用类型:slice,map,channel,interface,func,string等,声明一个引用类型的变量,编译器会把实例的内存分配在堆上。

          string和其他语言一样,是引用类型,string的底层实现struct String { byte* str; intgo len; }; 但是因为string不允许修改,每次操作string只能生成新的对象,所以在看起来使用时像值类型。

          其实引用类型可以看作对指针的封装。

          Slice切片在Go语言中实质是一种结构体类型,源码中定义如下:

          源码位置:src/runtime/slice.go

          type slice struct { array unsafe.Pointer len   int cap   int}

          从定义中我们可以知道slice是一种值类型,array是底层数组指针,它指向底层分配的数组;len是底层数组的元素个数;cap是底层数组的容量,超过容量会扩容。

          问题与解析

          典型问题

          有了上面知识的铺垫,下面我们来看下把slice作为函数参数传递的典型问题:

          package mainimport "fmt"func main() { tmp := make([]int, 0)    fmt.Printf("%p\n", &tmp) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp) change(tmp) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}func change(tmp []int) {    fmt.Printf("%p\n", &tmp) tmp = append(tmp, 6)    fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}//运行结果//0xc000004078//[] 0 0 0x59cde0//0xc0000040c0//[6] 1 1 0xc000014098//[] 0 0 0x59cde0

          这是一个典型问题,你所有疑问的基本这种类型的问题。

          疑问点:slice不是引用类型吗?把它做参数传递时实参应该同步修改啊,为什么main函数中的tmp没变?

          解析:

          从之前讲的知识中我们已经知道slice实质是一个结构体,其作为参数传递时形参实质复制了实参整个结构体的内容,其实就是值传递。

          形参分配有一份内存空间,存放和实参相同的内容,从运行结果可以看出形参的内存地址和实参是不同的。

          因为形参中底层数组指针和实参相同,所以当做修改操作时会同步修改到实参中,但是当使用append函数添加元素时,append函数返回的slice会覆盖修改到形参的内存空间中,和实参无关,所以在main函数中实参不变。可以在上面代码中看到函数中形参已变但实参未变。

          有同学看到上面解析之后可能还会有一些疑问,比如:

          append函数有扩容机制,当函数内使用append未扩容时,是不是就可以同步增加元素到实参中?
          为什么传指针就可以和实参完全同步,指针不也和引用类似吗?
          函数中使用append时,如果扩容,其中形参内存空间中底层数组的地址会被覆盖修改为新的扩容后的底层数组地址,而实参无变化。上面的代码就是如此。

          其它疑问1

          package mainimport "fmt"func main() { tmp := make([]int, 0, 5) tmp = append(tmp, 1, 2, 3) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp) change(tmp) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}func change(tmp []int) { tmp = append(tmp, 4) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}//[1 2 3] 3 5 0xc00000c300//[1 2 3 4] 4 5 0xc00000c300//[1 2 3] 3 5 0xc00000c300

          疑问点:从代码中可以看出函数中使用append时是没有扩容的,因为形参中底层数组地址和实参是一致的,那为什么实参中没有增加元素?

          解析:

          其实实参中tmp[3]已经变为4,但是实参和形参内存空间中len和cap是独立的,形参中len修改为了4但实参中len仍然为3,所以实参中未增加元素。

          关于tmp[3]已经变为4可以从如下代码中反映出来:

          package mainimport "fmt"func main() { tmp := make([]int, 0, 5) tmp = append(tmp, 1, 2, 3, 4, 5) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp) change(tmp[:3]) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}func change(tmp []int) { tmp = append(tmp, 6) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}//[1 2 3 4 5] 5 5 0xc00000c300//[1 2 3 6] 4 5 0xc00000c300//[1 2 3 6 5] 5 5 0xc00000c300

          可以看出实参中4已经变为6

          或者从如下代码中更为直接的看出:

          package mainimport ( "fmt" "unsafe")func main() { tmp := make([]int, 0, 5) tmp = append(tmp, 1, 2, 3) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp) change(tmp) p := unsafe.Pointer(&tmp[2]) q := uintptr(p) + 8 t := (*int)(unsafe.Pointer(q)) fmt.Println(*t) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}func change(tmp []int) { tmp = append(tmp, 4) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}//[1 2 3] 3 5 0xc00000c300//[1 2 3 4] 4 5 0xc00000c300//4//[1 2 3] 3 5 0xc00000c300

          用实参tmp[2]的地址往后移一个元素地址长度,得到tmp[3]的地址输出,可以看到变为了3。

          其它疑问2

          package mainimport "fmt"func main() { tmp := make([]int, 0, 5) tmp = append(tmp, 1, 2, 3) fmt.Printf("%p\n", &tmp) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp) change(&tmp) fmt.Printf("%v %d %d %p\n", tmp, len(tmp), cap(tmp), tmp)}func change(tmp *[]int) { *tmp = append(*tmp, 4) fmt.Printf("%p\n", tmp) fmt.Printf("%v %d %d %p\n", *tmp, len(*tmp), cap(*tmp), *tmp)}//0xc000004078//[] 0 0 0xffdde0//0xc000004078//[1] 1 1 0xc000014098//[1] 1 1 0xc000014098

          疑问点:为什么指针可以同步修改到实参,*tmp = append(*tmp, 4)这不也是覆盖修改到形参吗?

          解析:

          首先明确传指针时传的是slice的地址,形参是地址而非一份和实参相同内容的内存空间,这点从代码中打印的0xc000004078地址可以看出。所以*tmp = append(*tmp, 4)这段代码覆盖修改的是0xc000004078这个地址指向的slice,即主函数中的tmp切片,这点从代码中主函数中切片tmp的底层数组地址从0xffdde0变为0xc000014098可以看出。

          结论

          当传指针时,对函数中slice的任何修改其实都是对主函数中slice的修改;当传引用,即slice本身时,对函数中slice使用append时的修改实际是对形参新分配内存空间的修改而实参不变,但当直接修改slice中值时能同步修改到实参中。

          感谢各位的阅读,以上就是“Go语言Slice作为函数参数的使用方法”的内容了,经过本文的学习后,相信大家对Go语言Slice作为函数参数的使用方法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

          --结束END--

          本文标题: Go语言Slice作为函数参数的使用方法

          本文链接: https://lsjlt.com/news/296949.html(转载时请注明来源链接)

          有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

          猜你喜欢
          • Go语言Slice作为函数参数的使用方法
            这篇文章主要讲解了“Go语言Slice作为函数参数的使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言Slice作为函数参数的使用方法”吧!目录前言问题与解析典型问题其它疑问1其...
            99+
            2023-06-20
          • 详解Go语言Slice作为函数参数的使用
            目录前言问题与解析典型问题其它疑问1其它疑问2结论参考链接前言 首先要明确Go语言中实质只有值传递,引用传递和指针传递是相对于参数类型来说。 个人认为上诉的结论不对,把引用类型看做...
            99+
            2024-04-02
          • shell 使用数组作为函数参数的方法(详解)
            因工作需要,需要使用shell开发一些小工具,当使用数组作为函数参数时,发现只能传递数组的第一个元素,数组后面的元素不能传递到函数内。 #!/bin/bash function showArr(){ ...
            99+
            2022-06-04
            数组 详解 函数
          • Go语言如何使用变长参数函数
            这篇文章主要介绍“Go语言如何使用变长参数函数”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言如何使用变长参数函数”文章能帮助大家解决问题。01 介绍Go 语言中函数的最后一个参数可以是变长参...
            99+
            2023-07-02
          • Go语言怎么使用变长参数函数
            目录01 介绍02 变长参数03 变长参数函数04 使用场景05 总结本文我们主要介绍在 Go 语言中怎么使用变长参数函数,先是介绍变长参数和变长参数函数的相关知识,然后列举了一个简...
            99+
            2024-04-02
          • Golang函数作为参数使用的方法是什么
            这篇文章主要讲解了“Golang函数作为参数使用的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang函数作为参数使用的方法是什么”吧!为什么需要将函数作为参数传递在Gola...
            99+
            2023-07-06
          • GO语言不固定参数函数与匿名函数的使用
            目录不固定参数函数匿名函数 不固定参数函数 在go语言中,允许对函数设置不固定参数。不过需要注意的是,虽然不限制参数数量,但限制了参数的数据类型。 从原理分析,不固定参数利...
            99+
            2023-03-19
            GO语言不固定参数函数 GO语言 匿名函数
          • Go语言中函数的使用方法详解
            目录函数特点函数知识点函数的使用函数定义函数的参数函数的返回值函数的变量作用域函数的递归调用函数的可变参数函数的闭包函数的 defer 语句注意函数特点 函数可以有多个返回值。可以将...
            99+
            2023-05-18
            Go语言函数使用方法 Go语言函数使用 Go语言函数
          • Go语言中slice的反转方法怎么使用
            这篇文章主要介绍了Go语言中slice的反转方法怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go语言中slice的反转方法怎么使用文章都会有所收获,下面我们一起来看看吧。使用for循环和切片的索引最基...
            99+
            2023-07-06
          • Go语言函数参数传递方式详解
            Go语言是一门由Google开发的开源编程语言,旨在提高开发人员的生产力和代码的可靠性。在Go语言中,函数参数的传递方式是非常重要的,它可以影响程序的性能和内存占用。 1. 值传递 值...
            99+
            2024-03-09
            go语言 函数参数 传递方式 内存占用
          • 探究Go语言中方法与函数的使用方法
            go语言提供了方法和函数两种机制来定义可重用代码块。方法用于在特定类型上操作数据,定义时需要显式指定接收者类型,使用点运算符调用。函数用于全局操作,定义与其他编程语言类似,使用括号调用。...
            99+
            2024-04-03
            方法 函数 go语言
          • c语言数组怎么作为参数传入函数
            在C语言中,数组作为参数传递给函数需要使用指针。具体步骤如下: 在函数声明中,将数组参数声明为指针类型。例如,如果数组类型为int...
            99+
            2024-02-29
            C语言
          • golang函数方法作为参数传递
            go 函数方法作为参数传递:go 函数可作为其他函数的参数传递。可以将代码组织成模块化且可重用的片段。实战案例:可以使用打印函数作为参数打印切片元素。 Go 语言中函数方法作为参数的传...
            99+
            2024-04-22
            golang 参数传递
          • Go语言中的函数与方法怎么使用
            这篇文章主要介绍了Go语言中的函数与方法怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go语言中的函数与方法怎么使用文章都会有所收获,下面我们一起来看看吧。函数定义语法与大部分语言一致,Go语言中的函数...
            99+
            2023-07-02
          • C语言中数组作为函数参数的示例分析
            这篇文章主要介绍了C语言中数组作为函数参数的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。数组作为函数参数1 冒泡排序函数的错误设计...
            99+
            2024-04-02
          • Go语言中的Slice链式操作方法是什么
            今天小编给大家分享一下Go语言中的Slice链式操作方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。示例首先模拟一个...
            99+
            2023-07-05
          • GO语言不固定参数函数与匿名函数如何使用
            这篇文章主要介绍“GO语言不固定参数函数与匿名函数如何使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“GO语言不固定参数函数与匿名函数如何使用”文章能帮助大家解决问题。不固定参数函数在go语言中,...
            99+
            2023-07-05
          • C语言函数的参数使用指针
            在c语言中实参和形参之间的数据传输是单向的“值传递”方式,也就是实参可以影响形参,而形参不能影响实参。指针变量作为参数也不例外,但是可以改变实参指针变量所指向的变量的值。 #in...
            99+
            2024-04-02
          • Go语言函数方法调用详解
            标题:Go语言函数方法调用详解 Go语言作为一种快速、简单、高效的编程语言,其函数方法调用是其中一个重要的特性。本文将详细介绍Go语言中函数方法的调用方式,并提供具体的代码示例,帮助读...
            99+
            2024-04-02
          • C语言函数参数怎么使用
            本篇内容主要讲解“C语言函数参数怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言函数参数怎么使用”吧!一、函数参数函数参数在本质上与局部变量相同在栈上分配空间函数参数的初始值是函数调...
            99+
            2023-06-30
          软考高级职称资格查询
          编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
          • 官方手机版

          • 微信公众号

          • 商务合作