空结构片的地址 [英] Addresses of slices of empty structs

查看:94
本文介绍了空结构片的地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在empty struct上有一个基本问题,试图在尝试获取两个切片的支持数组元素的地址时遇到的以下不同输出:

I have a basic question on empty structs and am trying to get my head around the following different outputs I am getting when attempting to get the address of backing arrays' elements for two slices:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])

上面的代码段返回:

&a == &b false
&a[0] == &b[0] true

但是,请考虑以下略有变化的代码段:

However, considering the following slightly changed snippet:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(a[0], &a[0])
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])

以上代码段返回:

{} &{}
&a == &b false
&a[0] == &b[0] false

有人可以解释上述差异的原因吗?谢谢!

Can someone please explain the reason for the above difference? Thank you!

[跟进] 进行以下修改:

package main

import "fmt"

type S struct{}

func (s *S) addr() { fmt.Printf("%p\n", s) }

func main() {
    a := make([]S, 10)
    b := make([]S, 20)
    fmt.Println(a[0], &a[0])
    fmt.Println("&a == &b", &a == &b)
    fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])
    //a[0].addr()
    //b[0].addr()
}

仍然返回相同的输出:

{} &{}
&a == &b false
&a[0] == &b[0] false

尽管取消了方法调用的注释,但返回:

Though, uncommenting the method calls, returns:

{} &{}
&a == &b false
&a[0] == &b[0] true
0x19583c // ==> [depends upon env]
0x19583c // ==> [depends upon env]

推荐答案

在深入了解之前,请了解根据规范,程序是正确的,无论对于大小为零的值产生的地址是相等还是不同,例如spec仅声明它们可以相同,但不要求它们相同.

Before going any deeper, know that according to the spec, a program is correct regardless of whether it yields equal or different addresses for values having zero size, as the spec only states that they may be the same, but does not require them to be the same.

规范:大小和对齐保证:

如果结构或数组类型不包含大小大于零的字段(或分别包含元素),则其大小为零.两个不同的零大小变量可能在内存中具有相同的地址.

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

因此,您所体验的是实现细节.有关决策的更多细节和因素,以下解释仅对您的具体示例有效并足够:

So what you experience is an implementation detail. There are more details and factors to the decisions made, the following explanation is valid and sufficient only to your concrete examples:

在您的第一个示例中,切片的后备数组的地址仅在main()函数内部使用,它们不会逸出到堆中.您打印的只是地址比较的结果.这些只是bool值,不包括地址值.因此,编译器选择对ab的后备数组使用相同的地址.

In your first example addresses of the backing array(s) for your slices are only used inside the main() function, they don't escape to the heap. What you print is only the result of the comparison of the addresses. These are just bool values, they don't include the address values. So the compiler chooses to use the same address for the backing array of a and b.

在您的第二个示例中,在main()函数的 outside 中使用了后备数组的地址(更具体地说,是后备数组的某些元素的地址),它们被传递到fmt.Println()功能,因为您也在打印这些地址.

In your second example addresses of the backing arrays (more specifically addresses of some elements of the backing arrays) are used outside of the main() function, they are passed to and used inside the fmt.Println() function, because you are also printing these addresses.

我们可以通过将-gcflags '-m'参数传递给Go工具来证明"这一点,要求它打印转义分析的结果.

We can "prove" this by passing the -gcflags '-m' params to the Go tool, asking it to print the result of the escape analysis.

在第一个示例中,将代码保存在play.go中,运行go run -gcflags '-m' play.go命令,输出为:

In your first example, saving your code in play.go, running the go run -gcflags '-m' play.go command, the output is:

./play.go:10:14: "&a == &b" escapes to heap
./play.go:10:29: &a == &b escapes to heap
./play.go:11:14: "&a[0] == &b[0]" escapes to heap
./play.go:11:38: &a[0] == &b[0] escapes to heap
./play.go:8:11: main make([]struct {}, 10) does not escape
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:26: main &a does not escape
./play.go:10:32: main &b does not escape
./play.go:10:13: main ... argument does not escape
./play.go:11:32: main &a[0] does not escape
./play.go:11:41: main &b[0] does not escape
./play.go:11:13: main ... argument does not escape
&a == &b false
&a[0] == &b[0] true

我们看到,地址不会逃脱.

As we can see, addresses don't escape.

在第二个示例中运行go run -gcflags '-m' play.go,输出为:

Running go run -gcflags '-m' play.go with your second example, the output is:

./play.go:10:15: a[0] escapes to heap
./play.go:10:20: &a[0] escapes to heap
./play.go:10:20: &a[0] escapes to heap
./play.go:8:11: make([]struct {}, 10) escapes to heap
./play.go:11:14: "&a == &b" escapes to heap
./play.go:11:29: &a == &b escapes to heap
./play.go:12:14: "&a[0] == &b[0]" escapes to heap
./play.go:12:38: &a[0] == &b[0] escapes to heap
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:13: main ... argument does not escape
./play.go:11:26: main &a does not escape
./play.go:11:32: main &b does not escape
./play.go:11:13: main ... argument does not escape
./play.go:12:32: main &a[0] does not escape
./play.go:12:41: main &b[0] does not escape
./play.go:12:13: main ... argument does not escape
{} &{}
&a == &b false
&a[0] == &b[0] false

如您所见,a[0]&a[0]转储到堆中,因此动态分配了a的后备数组,因此它的地址将与b的地址不同.

As you can see, a[0], &a[0] escape to heap, so the backing array of a is dynamically allocated, and thus it will have a different address than that of b's.

让我们进一步证明"这一点.让我们修改第二个示例,以使第三个变量c的地址也不会被打印,然后将bc进行比较:

Let's "prove" this further. Let's modify your second example, to have a third variable c whose address will also not be printed, and let's compare b to c:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
c := make([]struct{}, 30)
fmt.Println(a[0], &a[0])
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])
fmt.Println("&b == &c", &b == &c)
fmt.Println("&b[0] == &c[0]", &b[0] == &c[0])

为此运行go run -gcflags '-m' play.go,输出为:

./play.go:11:15: a[0] escapes to heap
./play.go:11:20: &a[0] escapes to heap
./play.go:11:20: &a[0] escapes to heap
./play.go:8:11: make([]struct {}, 10) escapes to heap
./play.go:12:14: "&a == &b" escapes to heap
./play.go:12:29: &a == &b escapes to heap
./play.go:13:14: "&a[0] == &b[0]" escapes to heap
./play.go:13:38: &a[0] == &b[0] escapes to heap
./play.go:14:14: "&b == &c" escapes to heap
./play.go:14:29: &b == &c escapes to heap
./play.go:15:14: "&b[0] == &c[0]" escapes to heap
./play.go:15:38: &b[0] == &c[0] escapes to heap
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:11: main make([]struct {}, 30) does not escape
./play.go:11:13: main ... argument does not escape
./play.go:12:26: main &a does not escape
./play.go:12:32: main &b does not escape
./play.go:12:13: main ... argument does not escape
./play.go:13:32: main &a[0] does not escape
./play.go:13:41: main &b[0] does not escape
./play.go:13:13: main ... argument does not escape
./play.go:14:26: main &b does not escape
./play.go:14:32: main &c does not escape
./play.go:14:13: main ... argument does not escape
./play.go:15:32: main &b[0] does not escape
./play.go:15:41: main &c[0] does not escape
./play.go:15:13: main ... argument does not escape
{} &{}
&a == &b false
&a[0] == &b[0] false
&b == &c false
&b[0] == &c[0] true

由于仅打印&a[0],而不打印&b[0]&c[0],因此&a[0] == &b[0]将是false,而&b[0] == &c[0]将是true.

Since only &a[0] is printed but not &b[0] nor &c[0], thus &a[0] == &b[0] will be false but &b[0] == &c[0] will be true.

这篇关于空结构片的地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆