Go struct 不能犯的一个低级错误!
一段代码的结果是 false,第二段的结果是 true,且可以看到内存地址指向的完全一样,也就是排除了输出后变量内存指向改变导致的原因。 进一步来看,似乎是 fmt.Print 方法导致的,但一个标准库里的输出方法,会导致这种奇怪的问题? 问题剖析如果之前有被这个 “坑” 过,或有看过源码的同学。可能能够快速的意识到,导致这个输出是逃逸分析所致的结果。 我们对例子进行逃逸分析:分析可得知变量 a, b 均是分配在栈中,而变量 c, d 分配在堆中。 其关键原因是因为调用了 fmt.Println 方法,该方法内部是涉及到大量的反射相关方法的调用,会造成逃逸行为,也就是分配到堆上。 为什么逃逸后相等 关注第一个细节,就是 “为什么逃逸后,两个空 struct 会是相等的?”。 这里主要与 Go runtime 的一个优化细节有关,如下:量 zerobase 是所有 0 字节分配的基础地址。更进一步来讲,就是空(0字节)的在进行了逃逸分析后,往堆分配的都会指向 zerobase 这一个地址。 所以空 struct 在逃逸后本质上指向了 zerobase,其两者比较就是相等的,返回了 true。 为什么没逃逸不相等关注第二个细节,就是 “为什么没逃逸前,两个空 struct 比较不相等?了一句很经典的,细品: Pointers to distinct zero-size variables may or may not be equal. 另外空 struct 在实际使用中的场景是比较少的,常见的是:
但业务场景的情况下,也大多数会随着业务发展而不断改变,假设有个远古时代的 Go 代码,依赖了空 struct 的直接判断,岂不是事故上身? 不可直接依赖因此 Go 团队这番操作,与 Go map 的随机性如出一辙,避免大家对这类逻辑的直接依赖,是值得思考的。 而在没逃逸的场景下,两个空 struct 的比较动作,你以为是真的在比较。实际上已经在代码优化阶段被直接优化掉,转为了 false。 因此,虽然在代码上看上去是 == 在做比较,实际上结果是 a == b 时就直接转为了 false,比都不需要比了。 你说妙不? 没逃逸让他相等既然我们知道了他是在代码优化阶段被优化的,那么相对的,知道了原理的我们也可以借助在 go 编译运行时的 gcflags 指令,让他不优化。
在运行前面的例子时,执行 -gcflags="-N -l" 指令: (编辑:常州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |