[Go]零大小的变量/空结构体及其对应的切片/数组类型中的元素可能具有相同的地址

最近在修复grpc-gateway 的一个 bug时,发现原作者写的测试用例根本就是无效的(总是通过)。 原因却是因为其代码“触碰”到了 Golang 中关于取地址的一个有点匪夷所思的“特性”。

问题描述

作者定义了一个具有 3 个空结构体的切片(数组),然后对这 3 个元素分别取地址,期望得到不同的地址,但是结果却是不行的。 我把原问题简化并抽象出来了(Go Playground):

package main

import (
	"fmt"
)

type S struct {
}

type T struct {
	b bool
}

func main() {
	var s [3]S
	var t [3]T

	fmt.Printf("%p,%p,%p\n", &s[0], &s[1], &s[2])
	fmt.Printf("%p,%p,%p\n", &t[0], &t[1], &t[2])
}

输出结果:

0x58fd18,0x58fd18,0x58fd18
0xc00009400b,0xc00009400c,0xc00009400d

具体的值不重要,重要的是:前三者相同,后三者不同。

规范是怎样的?

到了这里,我其实也是非常疑惑的。 如果仅是几个普通的结构体变量,其地址相同的话,那还可以接受。 居然,居然切片/数组中的不同元素的地址也相同?简直太不可思议了。

后来我查了语言规范(位于规范的最后一行)中关于大小和对齐的章节, 原文如是说:

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.

两个零大小的不同的变量在内存中可能有相同的地址。

看起来像那么回事,但是没有直接说明位于切片/数组中的元素也具体和变量类似的相同的地址。

关于这一点,Golang 在语言标准/实现上似乎跟其它语言(比如 C++)不同, C++ 明确规定:

  • 空结构体的大小至少是 1
  • new 出来的两个新对象具有不同的地址

几年前,我在文章「一个空类/结构体的大小是多少?」中也说明过这一点。

最后

这算不算是 Golang 的 BUG?这样设计的意义何在?

如果文章有帮助到你,请我喝杯冰可乐吧~

发表于:2020年7月7日 ,阅读量:110 ,标签:C++ · Go