Empty structure triggered by large face hit scene

background

Hello everyone, I am Asong who is learning PHOTOSHOP technology. Last week, readers asked me a question, which I think is quite meaningful, so I will share it here. Let’s take a look at this question first:

type User struct{}func FPrint(u User) {
	fmt.Printf("FPrint %p\n", &u)
}

func main(a) {
	u := User{}
	FPrint(u)
	fmt.Printf("main: %p\n", &u)
}
// Run the result
FPrint 0x118eff0
main: 0x118eff0
Copy the code

Watching the results, most of my friends should be like me, confused? Isn’t the Go language just value passing? I also wrote an article earlier on “Are Go language parameters passed by value or by reference?” , has come to a clear conclusion that the Go language is indeed value pass-only, which is not a slap in the face…

Since such a result has appeared, it is necessary to give a reasonable explanation, so as not to make the atmosphere embarrassing, so I give my guess, as follows:

  • Guess one: this is abug
  • Hypothesis two: the special properties of the structure

Guess one is a little unimaginative, and I can’t prove it yet, so let’s test guess two. Please start my show.

Test hypothesis two: the special properties of the structure

In the above problem, the pass parameter is an empty structure. What if the pass parameter is a structure with fields? Let’s take a look:

type UserIsEmpty struct{}type UserHasField struct {
	Age uint64 `json:"age"`
}

func FPrint(uIsEmpty UserIsEmpty, uHasField UserHasField) {
	fmt.Printf("FPrint uIsEmpty:%p uHasField:%p\n", &uIsEmpty, &uHasField)
}

func main(a) {
	uIsEmpty := UserIsEmpty{}
	uHasField := UserHasField{
		Age: 10,
	}
	FPrint(uIsEmpty, uHasField)
	fmt.Printf("main: uIsEmpty:%p uHasField:%p\n", &uIsEmpty, &uHasField)
}
// Result:
FPrint uIsEmpty:0x118fff0 uHasField:0xc0000ba008
main: uIsEmpty:0x118fff0 uHasField:0xc0000ba000
Copy the code

If you run the go tool compile-n -l -s test.go command, you can use the compile tool compile-n -l -s test.go command, you can use the compile tool compile-n -l -s test.go command, you can use the compile tool compile-n -l -s test.go command.

The result is a call to Runtime.newObject (SB) to allocate memory. Follow this to find its implementation in runtme/malloc.go:

func newobject(typ *_type) unsafe.Pointer {
	return mallocgc(typ.size, typ, true)}Copy the code

Newobject () mainly calls the mallocGC () method, and here I find the answer. Because the mallocGC () code is quite long, I’ve taken the key part here:

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
	if gcphase == _GCmarktermination {
		throw("mallocgc called with gcphase == _GCmarktermination")}if size == 0 {
		return unsafe.Pointer(&zerobase)
	}
..........
}
Copy the code

If size is 0, the address of the global variable zerobase is returned. At this point, there may be some partners confused, what does this have to do with the above question? Structs take up a small chunk of memory, and their size is aligned with their length, but “empty structs” don’t take up memory, and their size is 0. Now everything can be said to be clear, to sum up the reasons:

The empty structure does not occupy memory, so the size is 0. During memory allocation, the address of Zerobase will be uniformly returned when the size is 0. Therefore, the address of the empty structure will be the same after the value copy occurs when the parameter is passed, resulting in the illusion that Go is not value pass.

The properties of empty structures extend

Empty structs are memory aligned when they are used as a field inside a structure.

Let’s start with an example:

func main(a){	
	fmt.Println(unsafe.Sizeof(Test1{}))
	fmt.Println(unsafe.Sizeof(Test2{}))
	fmt.Println(unsafe.Sizeof(Test3{}))

}

type Test1 struct {
	s struct{}
	n byte
	m byte
}

type Test2 struct {
	n byte
	s struct{}
	c byte
}

type Test3 struct {
	b byte
	s struct{}}// Run the result
2
2
2
Copy the code

According to the operation results, we can draw a conclusion:

Struct {} as the last field, it will be filled to the size of the previous field, and the address offset alignment rule will not change.

conclusion

Finally, make a full summary:

  1. The empty structure is also a structure, but hissizeZero, all empty structures are allocated to the same address, all of themzerobaseThe address;
  2. When the empty structure is used as an embedded field, the order of placement should be paid attention to. When the empty structure is used as the last field, special padding will be carried out. It will be filled and aligned to the size of the previous field.

Well, that’s all for this article, the three qualities (share, like, read) are the author’s motivation to continue to create more quality content!

We have created a Golang learning and communication group. Welcome to join the group and we will learn and communicate together. Join the group: add me vX pull you into the group, or the public number to get into the group two-dimensional code

At the end, I will send you a small welfare. Recently, I was reading the book [micro-service architecture design mode], which is very good. I also collected a PDF, which can be downloaded by myself if you need it. Access: Follow the public account: [Golang Dreamworks], background reply: [micro service], can be obtained.

I have translated a GIN Chinese document, which will be maintained regularly. If you need it, you can download it by replying to [GIN] in the background.

Translated a Machinery Chinese document, will be regularly maintained, there is a need for friends to respond to the background [Machinery] can be obtained.

I am Asong, an ordinary programming ape. Let’s get stronger together. We welcome your attention, and we’ll see you next time

Recommended previous articles:

  • Unsafe package
  • Source analysis panic and recover, do not understand you hit me!
  • Atomic Operations: The basics of concurrent programming
  • Detail the implementation mechanism of defer
  • You really understand interface
  • Leaf-segment Distributed ID Generation System (Golang implementation version)
  • 10 GIFs to help you understand sorting algorithms (with go implementation code)
  • Go parameter transfer type
  • Teach my sister how to write message queues
  • Cache avalanche, cache penetration, cache breakdown
  • Context package, read this article enough!!
  • Traffic Limiting Strategies for High Concurrency systems: Leaky buckets and token buckets
  • Interviewer: Have you used for-range in go? Can you explain the reasons for these problems