My code is below. When I check the address of the values it is stored as 280 288 290 298
. Why is it stored in this pattern?
package main
import "fmt"
func main() {
const a = 1111111111111111111
test := [7]int{a, 1, 33333,4,6,7,7}
fmt.Println(&test[0])
fmt.Println(&test[1])
fmt.Println(&test[2])
fmt.Println(&test[3])
fmt.Println(&test[4])
fmt.Println(&test[5])
fmt.Println(&test[6])
}
output :
0xc00000e280
0xc00000e288
0xc00000e290
0xc00000e298
0xc00000e2a0
0xc00000e2a8
0xc00000e2b0
Unsurprisingly, Go arrays are laid out contiguously in memory. Then since Go types are statically sized, the address of the nth item is equal to the address of the 0th element plus a byte offset equal to the size of the type of the item.
This can be roughly formalized as (pseudo code):
addr_n = addr_0 + (n * size_of(item))
And in Go code, using unsafe.Add
(since Go 1.17):
func main() {
const a = 1111111111111111111
x := [7]int{a, 1, 33333, 4, 6, 7, 7}
unsafePointer := unsafe.Pointer(&x[0])
for i := range x {
step := unsafe.Sizeof(int(0))
addr_n := unsafe.Add(unsafePointer, int(step)*i)
fmt.Printf("addr: %p, val: %d\n", addr_n, *(*int)(addr_n))
}
}
Which prints:
addr: 0xc000102000, val: 1111111111111111111
addr: 0xc000102008, val: 1
addr: 0xc000102010, val: 33333
addr: 0xc000102018, val: 4
addr: 0xc000102020, val: 6
addr: 0xc000102028, val: 7
addr: 0xc000102030, val: 7
In case it wasn't already crystal clear, the hex number is the memory address. This is how pointers are usually formatted by the fmt
package.
However note that the size of int
in particular is platform-dependent, hence in the snippet above you can't just add 8
. To make it deterministic, you can use unsafe.Sizeof(int(0))
.
Playground: https://go.dev/play/p/4hu8efVed96