Go study notes - three kinds of slices of special status

Today we want to tell a very little knowledge of the details of the Go language knowledge by most developers ignored, it is the state of three special sections - "zero slice", "Empty Tiles" and "nil slice."

6943526-0aee8636f5a7e1c8.png

Go language sections is considered the most important basic data structures, very simple to use, it became interesting internal structure of the Go language interview the most common test sites. The bottom layer is an array slice, slice is a surface structure comprising three variables, when we assign one slice to another slice, the slice is essentially a shallow copy of the surface structure. Structure of the first variable is a pointer to the bottom of the array, the other two variables are the length and capacity of the slices.

type slice struct {
  array unsafe.Pointer
  length int
  capcity int
}

Today we talk about the special status of one of "zero slice" is not really anything special sections, it just means that the underlying binary content of the array are zero. For example, the following code s variable is a "zero slice"

var s = make([]int, 10)
fmt.Println(s)
------------
[0 0 0 0 0 0 0 0 0 0]

If the slice is a pointer type, then the content on the whole underlying array is nil

var s = make([]*int, 10)
fmt.Println(s)
------------
[<nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>]

Zero slice is quite easy to understand, this part I will no longer continue in the form of self-torture into a dead end.

Here we want to introduce "Empty Tiles" and "nil slice" to understand the difference between them before we take a look at a slice of length zero out all those forms can be created

var s1 []int
var s2 = []int{}
var s3 = make([]int, 0)
// new 函数返回是指针类型,所以需要使用 * 号来解引用
var s4 = *new([]int)

fmt.Println(len(s1), len(s2), len(s3), len(s4))
fmt.Println(cap(s1), cap(s2), cap(s3), cap(s4))
fmt.Println(s1, s2, s3, s4)

----------------
0 0 0 0
0 0 0 0
[] [] [] []

From the above four forms of output, it seems exactly the same, no difference. But in fact there is a difference, we want to say two special types of "Empty Tiles" and "nil slice", hidden in the four forms of the above.

How do we analyze the difference between the internal structure of the four forms of the three sides of it? Took over to use advanced content Go to language, to translate the language of any variable type Go through unsafe.Pointer.

Since the internal structure of a slice is a structure comprising three integer variable word size of the machine, wherein a first variable is a pointer variable is a pointer variable to store an integer value, but this value is another variable memory address. We can use this structure as the length of the integer array 3 [3] int. Sections were then converted into variable [3] int.

var s1 []int
var s2 = []int{}
var s3 = make([]int, 0)
var s4 = *new([]int)

var a1 = *(*[3]int)(unsafe.Pointer(&s1))
var a2 = *(*[3]int)(unsafe.Pointer(&s2))
var a3 = *(*[3]int)(unsafe.Pointer(&s3))
var a4 = *(*[3]int)(unsafe.Pointer(&s4))
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)

---------------------
[0 0 0]
[824634199592 0 0]
[824634199592 0 0]
[0 0 0]

From the output we see the obvious magical feel different results unexpected difficult to understand. If the above unsafe code that you can not understand, and so I would continue the "Go fast to learn the language" section to update it.

The output of which is [000] of s1 and s4 variable is "nil slice", s2 and s3 variable is the "empty slice." 824 634 199 592 This value is a special memory address, all types of "empty slice" share this memory address. The following code three empty sections are pointing to the same memory address.

var s2 = []int{}
var s3 = make([]int, 0)

var a2 = *(*[3]int)(unsafe.Pointer(&s2))
var a3 = *(*[3]int)(unsafe.Pointer(&s3))
fmt.Println(a2)
fmt.Println(a3)

var s5 = make([]struct{ x, y, z int }, 0)
var a5 = *(*[3]int)(unsafe.Pointer(&s5))
fmt.Println(a5)

--------
[824634158720 0 0]
[824634158720 0 0]
[824634158720 0 0]

Represented graphically "empty slice" and "nil slice" as follows

6943526-976072db8b322a92.png

Empty Tiles pointed zerobase memory address is a magical address, you can see its definition from the Go language source code

//// runtime/malloc.go

// base address for all 0-byte allocations
var zerobase uintptr

// 分配对象内存
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    ...
    if size == 0 {
        return unsafe.Pointer(&zerobase)
    }
    ...
}

//// runtime/slice.go
// 创建切片
func makeslice(et *_type, len, cap int) slice {
     ...
     p := mallocgc(et.size*uintptr(cap), et, true)
     return slice{p, len, cap}
}

The last question was: "nil slice" and "Empty Tiles" What is the difference in the use of it?

The answer is absolutely no difference! No! No, there is a small difference! Consider the following code

package main

import "fmt"

func main() {
    var s1 []int
    var s2 = []int{}

    fmt.Println(s1 == nil)
    fmt.Println(s2 == nil)

    fmt.Printf("%#v\n", s1)
    fmt.Printf("%#v\n", s2)
}

-------
true
false
[]int(nil)
[]int{}

So in order to avoid the time of writing the best way to put his head out faint it is not to create "empty slice" consistent use "nil slice", while avoiding the slicing and comparing nil perform certain logic. This is the official standard recommendation.

The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.

"Empty Tiles" and "nil slice" is sometimes hidden in the structure, this time the difference between them was too many people ignore, let's look at an example

type Something struct {
    values []int
}

var s1 = Something{}
var s2 = Something{[]int{}}
fmt.Println(s1.values == nil)
fmt.Println(s2.values == nil)

--------
true
false

The results can be found in these two ways to create structure is not the same! The first no-argument constructor creates a nil slice, while the second creates an empty slice.

"Empty Tiles" and "nil slice" there is a very different place that JSON serialization

type Something struct {
    Values []int
}

var s1 = Something{}
var s2 = Something{[]int{}}
bs1, _ := json.Marshal(s1)
bs2, _ := json.Marshal(s2)
fmt.Println(string(bs1))
fmt.Println(string(bs2))

---------
{"Values":null}
{"Values":[]}

Ban! Ban! Ban! Json serialization their results actually are not the same!

Reproduced in: https: //www.jianshu.com/p/eccc2aa3d0bd

Guess you like

Origin blog.csdn.net/weixin_34414650/article/details/91051983