当前位置: 首页 > 知识库问答 >
问题:

Go语言中0长度的切片和数组

钦永贞
2023-03-14

试图找出0长度数组和切片在Golang中的行为。提出了两段代码(我在某处找到了代码,并对其进行了一些修改以处理此问题)

https://play.golang.org/p/ew2YYgvpGC

https://play.golang.org/p/jm2p6L6WCG

我从网站上了解到nil数组([]int(nil))的指针值为nil,所以我决定测试一下。果然,就是这样。我只是对制作和切片数组感到困惑。它对我来说有意想不到的行为。

我真的被这两个人的行为弄糊涂了。第一个在我的电脑和操场上运行良好。我注意到第一个和最后一个数组的地址总是完全相同的?为什么啊?

为什么会这样?

第二个更奇怪。这段代码与前一段代码完全相同,只是中间有len/cap的其他代码片段。它不在围棋场上运行,最后一个切片数组出现错误,出于某种原因,切片的长度为3(在我的计算机上,最后一个切片的长度为0,所有切片的上限为272851504)。但它确实在我的电脑上运行。我注意到用make创建的第一个数组的地址总是比最后一个小。它总是不同的,而且有点小(第一个),为什么?数组地址的代码没有变化

还有,为什么make()甚至创建了一个数组?长度为0的数组在内存中的外观如何?

共有1个答案

陶宏浚
2023-03-14

我注意到第一个和最后一个数组的地址总是完全相同的?为什么?

为什么会这样?

函数中的地址基于函数参数中的片头。偶然的是,该函数每次都在相同的内存地址上运行。此外,作为一个实现细节,当您将原始文件切片到零长度和容量时,它不会将数据指针归零。在这两个函数中,切片标头都被复制了,所以一开始甚至没有检查原始标头切片。

第二个例子,您没有正确地读出片头。如果你想玩,你不需要尝试做指针算术,你可以直接获取切片的地址

hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))

不过,如果您真的想通过指针算法检查切片头,也许这可以更好地说明您需要做什么:https://play.golang.org/p/GL6NtyPNs8

func InspectSlice(slice *[]int) {
    // Get the header directly by converting it to a reflect.SliceHeader
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(slice))

    // Create a header for comparison via pointer manipulation
    address := (uintptr)(unsafe.Pointer(slice))
    lenAddr := address + unsafe.Sizeof(address)
    capAddr := lenAddr + unsafe.Sizeof(int(0))

    unsafeHdr := reflect.SliceHeader{
        Data: *(*uintptr)(unsafe.Pointer(address)),
        Len:  *(*int)(unsafe.Pointer(lenAddr)),
        Cap:  *(*int)(unsafe.Pointer(capAddr)),
    }

    fmt.Printf("Real Header:  %#v\n", *hdr)
    fmt.Printf("UnsafeHeader: %#v\n", unsafeHdr)

}

还有,为什么make()会创建一个数组?

make不创建数组,您标记为数组的只是一个切片文本。

长度为0的数组在内存中的外观如何?

看起来什么都不像。它消耗0字节。

制作零长度切片时,您看到的很可能是指向全局零值数组的指针。制作一堆不同类型的零长度切片,数据指针都是相同的。正如所有空的struct{}和空的nilinterface{}被赋予与优化相同的值一样。这完全是一个实现细节,因为您不需要访问该数据值。当将一个片段切片回零长度和容量时,运行时也不会将数据指针归零。由于这两种行为,任何片中的数据指针很少为零。

 类似资料:
  • 主要内容:从数组或切片生成新的切片,直接声明新的切片,使用 make() 函数构造切片切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/ C++ 中的数组类型,或者 Python 中的 list 类型),这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内。 Go语言中切片的内部结构包含地址、大小和容量,切片一般用于快速地操作一块数据集合,如果将数据集合比作切糕的话,切片就是你要的“

  • 本位主要介绍一下 Go 语言中可变长度的"数组"——切片(slice)。数组有数组的用处,但是其不可变长度的特性,注定了在大多场景下不是很受欢迎。在大多数场景下我们都会选择更加灵活的切片。 1. 切片的创建 切片的声明方式和数组类似,写法上看就是声明一个没有长度的数组:var 切片名 []切片类型。其中切片类型可以是切片本身,也就是切片的切片,就构成了多维的切片。 切片在使用之前必须要初始化,它没

  • 问题内容: 我在4Gb机器的64位linux操作系统中运行以下代码: 当我运行它时,我得到: 如果我更改,我将得到: 当我使用片大小的内存时,我原本希望如此,但是当我尝试使用时,我得到: 因此,显然我无法创建大小为的切片,这使我们想到了一个问题:如果内存不是问题,那么我在Go中无法创建的最大切片是什么? 我记得在Java中,原始数组索引是通过type来管理的,因此,原始数组的最大大小是的最大值,如

  • Go 语言切片是对数组的抽象。 Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。 定义切片 你可以声明一个未指定大小的数组来定义切片: var identifier []type 切片不需要说明长度。 或使用make()函数来创建切片: var s

  • Go语言中同样允许使用多维切片,声明一个多维数组的语法格式如下: var sliceName [][]...[]sliceType 其中,sliceName 为切片的名字,sliceType为切片的类型,每个 代表着一个维度,切片有几个维度就需要几个 。 下面以二维切片为例,声明一个二维切片并赋值,代码如下所示。 上面的代码也可以简写为下面的样子。 上面的代码中展示了一个包含两个元素的外层切片,同

  • Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。 copy() 函数的使用格式如下: copy( destSlice, srcSlice []T) int 其中 srcSlice 为数据来源切片,destSlice 为复制的目标(也就是将 srcSlice 复制到 destSlice),目