草庐IT

Go 语言数组和切片的区别

AlwaysBeta 2023-04-02 原文

原文链接: Go 语言数组和切片的区别

在 Go 语言中,数组和切片看起来很像,但其实它们又有很多的不同之处,这篇文章就来说说它们到底有哪些不同。

另外,这个问题在面试中也经常会被问到,属于入门级题目,看过文章之后,相信你会有一个很好的答案。

数组

数组是同一种数据类型元素的集合,数组在定义时需要指定长度和元素类型。

例如:[4]int 表示一个包含四个整数的数组,数组的大小是固定的。并且长度是其类型的一部分([4]int[5]int 是不同的、不兼容的类型)。

数组元素可以通过索引来访问,比如表达式 s[n] 表示访问第 n 个元素,索引从零开始。

声明以及初始化

func main() {
    var nums [3]int   // 声明并初始化为默认零值
    var nums1 = [4]int{1, 2, 3, 4}  // 声明同时初始化
    var nums2 = [...]int{1, 2, 3, 4, 5} // ...可以表示后面初始化值的长度
    fmt.Println(nums)    // [0 0 0]
    fmt.Println(nums1)   // [1 2 3 4]
    fmt.Println(nums2)   // [1 2 3 4 5]
}

函数参数

如果数组作为函数的参数,那么实际传递的是一份数组的拷贝,而不是数组的指针。这也就意味着,在函数中修改数组的元素是不会影响到原始数组的。

package main

import (
    "fmt"
)

func Add(numbers [5]int) {
    for i := 0; i < len(numbers); i++ {
        numbers[i] = numbers[i] + 1
    }
    fmt.Println("numbers in Add:", numbers) // [2 3 4 5 6]
}

func main() {
    // declare and initialize the array
    var numbers [5]int
    for i := 0; i < len(numbers); i++ {
        numbers[i] = i + 1
    }

    Add(numbers)
    fmt.Println("numbers in main:", numbers) // [1 2 3 4 5]
}

切片

数组的使用场景相对有限,切片才更加常用。

切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。

切片是一种引用类型,它有三个属性:指针长度容量

  1. 指针:指向 slice 可以访问到的第一个元素。
  2. 长度:slice 中元素个数。
  3. 容量:slice 起始元素到底层数组最后一个元素间的元素个数。

底层源码定义如下:

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

声明以及初始化

func main() {
    var nums []int  // 声明切片
    fmt.Println(len(nums), cap(nums)) // 0 0
    nums = append(nums, 1)   // 初始化
    fmt.Println(len(nums), cap(nums)) // 1 1

    nums1 := []int{1,2,3,4}    // 声明并初始化
    fmt.Println(len(nums1), cap(nums1))    // 4 4

    nums2 := make([]int,3,5)   // 使用make()函数构造切片
    fmt.Println(len(nums2), cap(nums2))    // 3 5
}

函数参数

当切片作为函数参数时,和数组是不同的,如果一个函数接受一个切片参数,它对切片元素所做的更改将对调用者可见,类似于将指针传递给了底层数组。

package main

import (
    "fmt"
)

func Add(numbers []int) {
    for i := 0; i < len(numbers); i++ {
        numbers[i] = numbers[i] + 1
    }
    fmt.Println("numbers in Add:", numbers) // [2 3 4 5 6]
}

func main() {
    var numbers []int
    for i := 0; i < 5; i++ {
        numbers = append(numbers, i+1)
    }

    Add(numbers)

    fmt.Println("numbers in main:", numbers) // [2 3 4 5 6]
}

再看一下上面的例子,把参数由数组变成切片,Add 函数中的修改会影响到 main 函数。

总结

最后来总结一下,面试时也可以这么来回答:

  1. 数组是一个长度固定的数据类型,其长度在定义时就已经确定,不能动态改变;切片是一个长度可变的数据类型,其长度在定义时可以为空,也可以指定一个初始长度。
  2. 数组的内存空间是在定义时分配的,其大小是固定的;切片的内存空间是在运行时动态分配的,其大小是可变的。
  3. 当数组作为函数参数时,函数操作的是数组的一个副本,不会影响原始数组;当切片作为函数参数时,函数操作的是切片的引用,会影响原始切片。
  4. 切片还有容量的概念,它指的是分配的内存空间。

以上就是本文的全部内容,如果觉得还不错的话欢迎点赞转发关注,感谢支持。


参考文章:

推荐阅读:

有关Go 语言数组和切片的区别的更多相关文章

  1. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  4. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  5. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  6. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  7. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  8. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  9. ruby - 在 Ruby 中用键盘诅咒数组浏览 - 2

    我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作

  10. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

随机推荐