首页 > 解决方案 > 全局变量地址

问题描述

我只是在玩 Go 接口和结构,突然发现一些奇怪的东西。情况是这样的:

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

package main

import (
    "fmt"
)

func main() {
    scopedInt := 100
    fmt.Printf("%p\n", &scopedInt)
    
    globalInt = 100
    fmt.Printf("%p\n", &globalInt)
}

var globalInt int

输出:

0xc0000ba010
0x57b2a8

地址的值无关紧要。关键是为什么第一个地址的位数比第二个多?

我想我错过了关于 Go 中全局变量概念的一点。

标签: gopointersglobal-variables

解决方案


地址的“长度”相差很多位数,因为这些变量分配在内存的不同区域,这些区域具有不同的偏移量(起始位置)。

scopedInt可能会在堆上分配,因为它从main()(其地址传递到fmt.Printf())“逃逸”,而globalInt它是一个包级变量,因此将分配在固定大小的段之一,即数据段中。

只要地址指向有效的内存区域,地址的“长度”就无关紧要。Go 具有自动内存管理功能,因此除非您触摸 package unsafe,否则您不必担心地址和指针是否有效。

要了解有关内存管理的更多信息,请参阅Doug Richardson:Go Memory Management。引用它:

什么去哪里?

Go 编程语言规范没有定义项目的分配位置。例如,定义为的变量var x int可以分配在堆栈或堆上,并且仍然遵循语言规范。p同样, in指向的整数p := new(int)可以分配在堆栈或堆上。

但是,某些要求将在某些条件下排除某些内存选择。例如:

  • 数据段的大小在运行时不能改变,因此不能用于改变大小的数据结构。
  • 堆栈中项目的生命周期按它们在堆栈中的位置排序。如果堆栈的顶部是地址 X,那么 X 之上的所有内容都将被释放,而 X 之下的所有内容都将保持分配状态。如果被函数范围之外的项目引用,函数分配的内存可以转义该函数,因此不能在堆栈上分配(因为它仍在被引用),也不能在数据段中分配(因为数据段不能在运行时增长),因此它必须在堆上分配——尽管内联可以删除其中一些堆分配。

推荐阅读