[Golang] two objects have the same pointer. Must they be the same object?

Come to the point

Today, I found a very interesting case, as follows:

package main

import "fmt"

func main() {
	n1 := make ([] int, 0,5)
	n2 := n1[:2]
	fmt.Println(n1)
	fmt.Println(n2)
	// Consider whether n1 and n2 print the same pointer address?
	fmt.Printf("address of n1:%p\n",n1)
	fmt.Printf("address of n2:%p\n",n2)
}

n1 is not a pointer type, but a reference type of [] int slice of make. Most people may think that if the pointer address of two objects is the same, then the contents stored by the two objects are the same. Even if the Data field field (array) of sliceheader, the underlying array structure of slice, does not exceed the capacity of n1 by 5, It uses a Data field field with n2, but because the second field in sliceheader is Len, and the third field is Cap, n1 and n2. The sliceheader must not be the same, so the pointer address printed out with% p must be different. I started with this idea, and then I was surprised by the result, as follows:

[]
[0 0]
address of n1:0xc00007e030
address of n2:0xc00007e030

What? The printed pointer address is the same. I'm not calm for a moment, which seems to overturn my fixed idea that two objects have the same pointer address, so the two objects store the same content

guess

With my own doubts, the first thing I want to think about is to use reflection to turn slice into SliceHeader to find out whether the data, len and cap fields are the same:

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	n1 := make ([] int, 0,5)
	n2 := n1[:2]
	fmt.Println(n1)
	fmt.Println(n2)
	// Consider whether n1 and n2 print the same pointer address?
	fmt.Printf("address of n1:%p\n",n1)
	fmt.Printf("address of n2:%p\n",n2)
	sh:=(*reflect.SliceHeader)(unsafe.Pointer(&n1))
	fmt.Printf("n1 Data:%p,Len:%d,Cap:%d\n",sh.Data,sh.Len,sh.Cap)
	sh1:=(*reflect.SliceHeader)(unsafe.Pointer(&n2))
	fmt.Printf("n2 Data:%p,Len:%d,Cap:%d\n",sh1.Data,sh1.Len,sh1.Cap)
}

Output:

[]
[0 0]
address of n1:0xc00007e030
address of n2:0xc00007e030
n1 Data:%!p(uintptr=824634236976),Len:0,Cap:5
n2 Data:%!p(uintptr=824634236976),Len:2,Cap:5

Verification

At first, I saw the above results, which is still very confusing, because the pointers of n1 and n2 are the same, and the pointers of the Data field of uintptr are the same, but the pointer addresses of the two objects are the same in violation of the above rules, so the contents stored by the two objects are the same.
Well, when we get here, we can only manage point by point. The first thing I want to think about is whether the printed address of% p is really the pointer address of the object? Is it the storage address of the printed sliceHeader when it comes to slice? Then I turn to the official document of golang
https://golang.org/pkg/fmt/

Slice:
%p address of 0th element in base 16 notation, with leading 0x

Focus on the above mentioned explanation of slice's% p, and print the pointer address of the 0th element, which gives you a feeling of being suddenly aware and suddenly confused. It's a sudden realization that% p prints the address of the first element in the Data field, which is the 0 th element, so it's also natural. All of a sudden, the reason is that it should be the same as the Data pointer of uintptr printed by me?

Last question

After patting the head, I suddenly thought that% p printed with leading 0x, which is hexadecimal, and the Data field of SliceHeader is a pointer to uintptr

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

In other words, one printing is 16 forbidden, and the other printing is 10 forbidden. Convert the following base numbers:

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	n1 := make ([] int, 0,5)
	n2 := n1[:2]
	fmt.Println(n1)
	fmt.Println(n2)
	// Consider whether n1 and n2 print the same pointer address?
	fmt.Printf("address of n1:%p\n",n1)
	fmt.Printf("address of n2:%p\n",n2)
	sh:=(*reflect.SliceHeader)(unsafe.Pointer(&n1))
	fmt.Printf("n1 Data:Ox%x,Len:%d,Cap:%d\n",sh.Data,sh.Len,sh.Cap)
	sh1:=(*reflect.SliceHeader)(unsafe.Pointer(&n2))
	fmt.Printf("n2 Data:Ox%x,Len:%d,Cap:%d\n",sh1.Data,sh1.Len,sh1.Cap)
}

Output:

[]
[0 0]
address of n1:0xc00007e030
address of n2:0xc00007e030
n1 Data:Oxc00007e030,Len:0,Cap:5
n2 Data:Oxc00007e030,Len:2,Cap:5

It is indeed the same. slice's% p prints the pointer address of the Data field, pointing to the first element of the array of the Data field, that is, the starting address. All the above problems are solved. slice prints the same pointer address of% p, because the underlying Data field shares the same array. If you use append to expand the capacity beyond cap, you will use different Data field arrays, and the printed% p will be different

84 original articles published, 34 praised, 270000 visitors+
Private letter follow

Posted by nitharsanke on Tue, 10 Mar 2020 20:51:12 -0700