In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-20 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)06/01 Report--
Although golang is implemented in C and is called the next-generation C language, golang is still very different from C. It defines a rich set of data types and data structures, which are either directly mapped to C data types or implemented in C struct. Understanding the underlying implementation of golang's data types and data structures will help us better understand golang and write better quality code.
Basic type
The source code is: $GOROOT/src/pkg/runtime/runtime.h. Let's first take a look at the basic types:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
twenty
twenty-one
twenty-two
twenty-three
twenty-four
twenty-five
twenty-six
twenty-seven
twenty-eight
twenty-nine
thirty
thirty-one
/ *
* basic types
, /
Typedef signed char int8
Typedef unsigned char uint8
Typedef signed short int16
Typedef unsigned short uint16
Typedef signed int int32
Typedef unsigned int uint32
Typedef signed long long int int64
Typedef unsigned long long int uint64
Typedef float float32
Typedef double float64
# ifdef _ 64BIT
Typedef uint64 uintptr
Typedef int64 intptr
Typedef int64 intgo; / / Go's int
Typedef uint64 uintgo; / / Go's uint
# else
Typedef uint32 uintptr
Typedef int32 intptr
Typedef int32 intgo; / / Go's int
Typedef uint32 uintgo; / / Go's uint
# endif
/ *
* defined types
, /
Typedef uint8 bool
Typedef uint8 byte
Int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64 correspond to the type of C, which is easy to see as long as there is a C base. Uintptr and intptr are unsigned and signed pointer types, and ensure that it is 8 bytes on 64-bit platforms and 4 bytes on 32-bit platforms. Uintptr is mainly used for pointer operations in golang. Intgo and uintgo are not named int and uint because int is a type name in C, and uintgo must be used to correspond to the naming of intgo. Intgo and uintgo correspond to int and uint in golang. From the definition, you can see that int and uint are variable size types, accounting for 8 bytes on 64-bit platforms and 4 bytes on 32-bit platforms. So if there is a clear requirement, you should choose int32, int64 or uint32, uint64. The underlying type of the byte type is uint8. Take a look at the test:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
Package main
Import (
"fmt"
"reflect"
)
Func main () {
Var b byte ='D'
Fmt.Printf ("output:% v\ n", reflect.TypeOf (b) .Kind ())
}
?
one
two
three
four
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Output: uint8
Data types are divided into static types and underlying types. Relative to the variable b in the above code, byte is its static type and uint8 is its underlying type. This is very important, and this concept will be used frequently in the future.
Rune Typ
Rune is an alias for int32 and is used to represent unicode characters. You usually need to use it when dealing with Chinese, but you can also use the range keyword.
String Typ
At the bottom of the string type is a C struct.
?
one
two
three
four
five
Struct String
{
Byte* str
Intgo len
}
The member str is an array of characters and len is the length of an array of characters. Golang strings are immutable, and initialization of variables of type string means initialization of the underlying structure. As for why str uses the byte type instead of the rune type, this is because golang's for loop traverses the string based on bytes and, if necessary, can be converted to rune slices or iterated using range. Let's look at an example:
$GOPATH/src
-basictype_test
-main.go
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
Package main
Import (
"fmt"
"unsafe"
)
Func main () {
Var str string = "hi, Chen Yihui ~"
P: = (* struct {
Str uintptr
Len int
}) (unsafe.Pointer (& str))
Fmt.Printf ("% + v\ n", p)
}
?
one
two
three
four
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Output: & {str:135100456 len:14}
The operation of the built-in function len on the string type is to extract the lenvalue directly from the underlying structure without additional operation, of course, the value of len must be initialized at the same time of initialization.
Slice Typ
The underlying layer of the slice type is also a C struct.
?
one
two
three
four
five
six
Struct Slice
{/ / must not move anything
Byte* array; / / actual data
Uintgo len; / / number of elements
Uintgo cap; / / allocated number of elements
}
Including three members. Array is the underlying array, len is the number of actual storage, and cap is the total capacity. Use the built-in function make to initialize slice, or you can initialize it in an array-like way. When using the make function to initialize slice, the first parameter is slice type, the second parameter is len, and the third parameter is optional, if not passed in, cap equals len. The cap parameter is usually passed in to pre-allocate the size of the slice to avoid frequent memory reallocation.
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
Package main
Import (
"fmt"
"unsafe"
)
Func main () {
Var slice [] int32 = make ([] int32, 5,10)
P: = (* struct {
Array uintptr
Len int
Cap int
}) (unsafe.Pointer (& slice))
Fmt.Printf ("output:% + v\ n", p)
}
?
one
two
three
four
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Output: & {array:406958176 len:5 cap:10}
Because slices point to an underlying array, and slices can be generated directly from the array through slice syntax, you need to understand the relationship between slices and arrays, otherwise you may unwittingly write code with bug. For example, there is the following code:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
Package main
Import (
"fmt"
)
Func main () {
Var array = [...] int32 {1, 2, 3, 4, 5}
Var slice = array [2:4]
Fmt.Printf ("before changing slice: array=%+v, slice=%+v\ n", array, slice)
Slice [0] = 234
Fmt.Printf ("after changing slice: array=%+v, slice=%+v\ n", array, slice)
}
?
one
two
three
four
five
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Before changing slice: array= [1 2 3 4 5], slice= [3 4]
After changing slice: array= [1 2 234 4 5], slice= [2 34 4]
You can clearly see that after changing slice, array has also been changed. This is because the slices created by slice through the array point to the array, which means that the underlying array of the slice is the array. So obviously, the change to slice is to change its underlying array. Of course, if you delete or add elements, then the len will also change, and the cap may change.
So how does this slice point to array? The underlying array pointer to slice points to the element in array with index 2 (because the slice is generated through array [2:4]), len records the number of elements, and cap equals len.
The reason why cap may change is that cap represents total capacity, and adding or removing operations do not necessarily change total capacity. Let's move on to another example:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
Package main
Import (
"fmt"
)
Func main () {
Var array = [...] int32 {1, 2, 3, 4, 5}
Var slice = array [2:4]
Slice = append (slice, 6,7,8)
Fmt.Printf ("before changing slice: array=%+v, slice=%+v\ n", array, slice)
Slice [0] = 234
Fmt.Printf ("after changing slice: array=%+v, slice=%+v\ n", array, slice)
}
?
one
two
three
four
five
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Before changing slice: array= [1 2 3 4 5], slice= [3 4 6 7 8]
After changing slice: array= [1 234 5], slice= [234 4 6 7 8]
After the append operation, the changes to slice did not affect array. The reason is that the operation of append causes slice to reassign the underlying array, so the underlying array of slice no longer points to the previously defined array.
But obviously, this rule is the same for slices generated from slices, see the code:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
Package main
Import (
"fmt"
)
Func main () {
Var slice1 = [] int32 {1,2,3,4,5}
Var slice2 = slice1 [2:4]
Fmt.Printf ("before changing slice2: slice1=%+v, slice2=%+v\ n", slice1, slice2)
Slice2 [0] = 234
Fmt.Printf ("after changing slice2: slice1=%+v, slice2=%+v\ n", slice1, slice2)
}
?
one
two
three
four
five
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Before changing slice2: slice1= [1 2 3 4 5], slice2= [3 4]
After changing slice2: slice1= [1 2 234 4 5], slice2= [2 34 4]
Slice1 and slice2 share an underlying array, and modifying the elements of slice2 causes the slice1 to change as well.
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
Package main
Import (
"fmt"
)
Func main () {
Var slice1 = [] int32 {1,2,3,4,5}
Var slice2 = slice1 [2:4]
Fmt.Printf ("before changing slice2: slice1=%+v, slice2=%+v\ n", slice1, slice2)
Slice2 = append (slice2, 6,7,8)
Fmt.Printf ("after changing slice2: slice1=%+v, slice2=%+v\ n", slice1, slice2)
}
?
one
two
three
four
five
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Before changing slice2: slice1= [1 2 3 4 5], slice2= [3 4]
After changing slice2: slice1= [1 2 3 4 5], slice2= [3 4 6 7 8]
The append operation causes slice1 or slice2 to reassign the underlying array, so append operations on slice1 or slice2 do not affect each other.
Interface Typ
The implementation of the interface in golang is more complex, as defined in $GOROOT/src/pkg/runtime/type.h:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
Struct Type
{
Uintptr size
Uint32 hash
Uint8 _ unused
Uint8 align
Uint8 fieldAlign
Uint8 kind
Alg * alg
Void * gc
String * string
UncommonType * x
Type * ptrto
}
Defined in $GOROOT/src/pkg/runtime/runtime.h:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
Struct Iface
{
Itab* tab
Void* data
}
Struct Eface
{
Type* type
Void* data
}
Struct Itab
{
InterfaceType* inter
Type* type
Itab* link
Int32 bad
Int32 unused
Void (* fun []) (void)
}
Interface is actually a structure consisting of two members, one is a pointer to the data, and the other contains the type information of the member. Eface is the data structure used at the bottom of interface {}. Because type information is stored in interface, reflection can be implemented. Reflection is actually finding the metadata of the underlying data structure. The complete implementation is in: $GOROOT/src/pkg/runtime/iface.c.
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
Package main
Import (
"fmt"
"unsafe"
)
Func main () {
Var str interface {} = "Hello World!"
P: = (* struct {
Tab uintptr
Data uintptr
}) (unsafe.Pointer (& str))
Fmt.Printf ("% + v\ n", p)
}
?
one
two
three
four
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Output: & {tab:134966528 data:406847688}
Map Typ
The map implementation of golang is hashtable, and the source code is: $GOROOT/src/pkg/runtime/hashmap.c.
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
Struct Hmap
{
Uintgo count
Uint32 flags
Uint32 hash0
Uint8 B
Uint8 keysize
Uint8 valuesize
Uint16 bucketsize
Byte * buckets
Byte * oldbuckets
Uintptr nevacuate
}
The test code is as follows:
?
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
twenty
twenty-one
twenty-two
twenty-three
twenty-four
twenty-five
twenty-six
Package main
Import (
"fmt"
"unsafe"
)
Func main () {
Var m = make (map [string] int32, 10)
M ["hello"] = 123
P: = (* struct {
Count int
Flags uint32
Hash0 uint32
B uint8
Keysize uint8
Valuesize uint8
Bucketsize uint16
Buckets uintptr
Oldbuckets uintptr
Nevacuate uintptr
) (unsafe.Pointer (& m))
Fmt.Printf ("output:% + v\ n", p)
}
?
one
two
three
four
$cd $GOPATH/src/basictype_test
$go build
$. / basictype_test
Output: & {count:407032064 flags:0 hash0:134958144 barr 192 keysize:0 valuesize:64 bucketsize:30063 buckets:540701813 oldbuckets:0 nevacuate:0}
There are still many holes in golang, so we need to study the bottom layer deeply, otherwise it is easy to fall into the pit.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.