reflect.Value 的类型定义,下面是我机器上的代码路径。typ 存储了元素的真实类型, ptr 是元素实际存储的值
/usr/local/Cellar/go@1.13/1.13.12/libexec/src/reflect/value.go
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
// To compare two Values, compare the results of the Interface method.
// Using == on two Values does not compare the underlying values
// they represent.
type Value struct {
// typ holds the type of the value represented by a Value.
typ *rtype
// Pointer-valued data or, if flagIndir is set, pointer to data.
// Valid when either flagIndir is set or typ.pointers() is true.
ptr unsafe.Pointer
// flag holds metadata about the value.
// The lowest bits are flag bits:
// - flagStickyRO: obtained via unexported not embedded field, so read-only
// - flagEmbedRO: obtained via unexported embedded field, so read-only
// - flagIndir: val holds a pointer to the data
// - flagAddr: v.CanAddr is true (implies flagIndir)
// - flagMethod: v is a method value.
// The next five bits give the Kind of the value.
// This repeats typ.Kind() except for method values.
// The remaining 23+ bits give a method number for method values.
// If flag.kind() != Func, code can assume that flagMethod is unset.
// If ifaceIndir(typ), code can assume that flagIndir is set.
flag
// A method value represents a curried method invocation
// like r.Read for some receiver r. The typ+val+flag bits describe
// the receiver r, but the flag's Kind bits say Func (methods are
// functions), and the top bits of the flag give the method number
// in r's type's method table.
}
type flag uintptr
|
在着重强调一下 rtype 类型,在 Go 语言中,对于任意一种类型都存在一个与之对应的 rtype 类型
/usr/local/Cellar/go@1.13/1.13.12/libexec/src/reflect/type.go
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
|
上面这些最多也就是帮我们理解,接口类型其实包含实际的值,和实际的类型。下面才是我们核心的输出
结构体占用内存的大小
我们定义一个结构体类型,如何计算结构体类型占内存的大小呢?我们可以通过映射 rtype.size 来获取,这个计算结果是经过内存对齐之后的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package main
import (
"fmt"
"reflect"
"unsafe"
)
type tflag uint8
type nameOff int32
type typeOff int32
type ValueCopy struct {
typ *rtype
ptr unsafe.Pointer
flag uintptr
}
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
// a copy of runtime.typeAlg
type typeAlg struct {
// function for hashing objects of this type
// (ptr to object, seed) -> hash
hash func(unsafe.Pointer, uintptr) uintptr
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
}
type Animal struct {
Name string
Age int
}
func main() {
animal := Animal{
Name: "1",
Age: 2,
}
rv := reflect.ValueOf(animal)
realAnimal := (*ValueCopy)(unsafe.Pointer(&rv))
fmt.Println(realAnimal.typ.size)
// output: 24
}
|
结构体转[]byte切片
如何实现结构体转 byte 切片呢?结合上面的类型,我们可以实现最简单的转换。我们在上述代码的基础上追加了部分调整
首先我们将 []byte 转换为 SliceHeaderCopy 的内存结构,然后给转换后的 sli 进行赋值操作,我们就完成了结构体到 []byte 的转换。[]byte 中
存储的仍然是原 animal 的地址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
type SliceHeaderCopy struct {
Data unsafe.Pointer
Len int
Cap int
}
type Animal struct {
Name string
Age int
}
func main() {
animal := Animal{
Name: "1",
Age: 2,
}
rv := reflect.ValueOf(animal)
realAnimal := (*ValueCopy)(unsafe.Pointer(&rv))
var bytesContent []byte
sli := (*SliceHeaderCopy)(unsafe.Pointer(&bytesContent))
fmt.Println(realAnimal.typ.size)
// output: 24
sli.Data = realAnimal.ptr
sli.Len = int(realAnimal.typ.size)
sli.Cap = int(realAnimal.typ.size)
fmt.Println(bytesContent)
animalCopy := (*Animal)(unsafe.Pointer(&bytesContent[0]))
fmt.Println(animalCopy.Name, animalCopy.Age)
}
|
如果 animal 对象被删除,这个转换就会出现异常。因为 sli 中 Data 执行的地址发生了改变。因为 []byte 中存储的内容是:
[170 4 13 1 0 0 0 0 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0]
前16位标识的是 Name,后8位标识的是 Age。Name 中的内容是通过地址引用的,所以,当地址失效的话,通过 unsafe.Pointer 转换就会发生异常
String 内容存放到 []byte