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