A little unsafe but a little brighter

  Back end, golang, php

In the last article“a deeper understanding of gosice”, you will find that its underlying data structure uses theunsafe.Pointer. Therefore, I want to introduce some related knowledge again.

Original address:A little unsafe but a little brighter

Preface

When everyone learned Go, they must have learned the knowledge point “Go pointer does not support pointer operation and conversion”. Why?

First of all, Go is a static language. All variables must be of scalar type. Different types cannot perform cross-type operations such as assignment and calculation. Then the pointer also corresponds to the relative type, which is also within the scope of static type check of Compile. At the same time, static language is also called strong type. That is, once defined, it cannot be changed any more.

Error example

func main(){
    num := 5
    numPointer := &num

    flnum := (*float32)(numPointer)
    fmt.Println(flnum)
}

Output results:

# command-line-arguments
...: cannot convert numPointer (type *int) to type *float32

In the example, we created anumVariable, value 5, typeint. After taking its pointer address to, an attempt was made to cast to*float32, the result failed …

unsafe

For the “wrong example” just now, we can use today’s hero.unsafeStandard library to solve. It is a magical bag. In the official interpretation, it is summarized as follows:

  • Operations around Go Program Memory Security and Types
  • It is likely to be non-portable
  • Not protected by Go 1 compatibility guidelines

Simply put, it is not recommended to you. Because it is unsafe, but under special circumstances, it was used. Can break the type of Go and memory security mechanism, so that you can get a surprise effect at the moment.

Pointer

In order to solve this problem, need to useunsafe.Pointer. It represents any type of addressable pointer value and can be converted between different pointer types (similar to the use of void * in C language)

It includes four core operations:

  • Any type of Pointer value can be converted to a pointer
  • Pointer can be converted to any type of pointer value
  • Uintptr can be converted to Pointer
  • Pointer can be converted to uintptr

In this part, we will focus on the first and second points. Do you think again how to modify the “error example” to make it work?

func main(){
    num := 5
    numPointer := &num

    flnum := (*float32)(unsafe.Pointer(numPointer))
    fmt.Println(flnum)
}

Output results:

0xc4200140b0

In the above code, we made minor changes. viaunsafe.PointerThe pointer variable is modified by the property of the. to complete pointer conversion of any type (*T)

It should be noted that variables cannot be operated or accessed at this time. Because I don’t know what type the pointer address points to. I don’t know what type it is and how to analyze it. If it cannot be resolved, it will naturally be impossible to change it.

Offsetof

In the previous section, we modified ordinary pointer variables. So can it do something more complicated?

type Num struct{
    i string
    j int64
}

func main(){
    n := Num{i: "EDDYCJY", j: 1}
    nPointer := unsafe.Pointer(&n)

    niPointer := (*string)(unsafe.Pointer(nPointer))
    *niPointer = "煎鱼"

    njPointer := (*int64)(unsafe.Pointer(uintptr(nPointer) + unsafe.Offsetof(n.j)))
    *njPointer = 2

    fmt.Printf("n.i: %s, n.j: %d", n.i, n.j)
}

Output results:

n.i: 煎鱼, n.j: 2

Before analyzing what this code does, we need to understand some basic concepts of structures:

  • The member variable of the structure is a continuous memory on the memory storage
  • The initial address of the structure is the memory address of the first member variable
  • The offset is calculated based on the member address of the structure. The memory addresses of other member variables can be obtained

Looking back at the above code, we can see the execution process:

  • Modifyn.iValue:iIs the first member variable. Therefore, no offset calculation is required, and the pointer is directly fetched and converted intoPointer, and then cast to the pointer value of string type
  • Modifyn.jValue:jIs the second member variable. Offset calculation is required to modify its memory address. After the offset operation, the current address already points to the second member variable. Then repeat the conversion assignment

It should be noted that the following method is used here (to accomplish the target of offset calculation):

1、uintptr:uintptrIs the built-in type of Go. Returns an unsigned integer that stores a complete address. Subsequent operations are often used for pointer operations.

type uintptr uintptr

2. unsafe.Offsetof: returns the offset of member variable x in the structure. More specifically, it returns the number of bytes from the initial position of the structure to x. Attention should be paid to participationArbitraryTypeIndicates any type, not definedint. Its actual function is a placeholder

func Offsetof(x ArbitraryType) uintptr

In this part, it is actually a clever use.PointerThe third and fourth characteristics of. At this time, you can already operate on variables.

Error example

func main(){
    n := Num{i: "EDDYCJY", j: 1}
    nPointer := unsafe.Pointer(&n)
    ...

    ptr := uintptr(nPointer)
    njPointer := (*int64)(unsafe.Pointer(ptr + unsafe.Offsetof(n.j)))
    ...
}

There is a problem here.uintptrTypes cannot be stored in temporary variables. Because from a GC perspective,uintptrA temporary variable of type is only an unsigned integer, and it is not known to be a pointer address.

Therefore, when certain conditions are met,ptrThis temporary variable is likely to be garbage collected, so the following memory operations will not become a mystery?

Summary

Briefly review two knowledge points. The first isunsafe.PointerYou can make your variables move around in different pointer types, that is, any addressable pointer type. The second isuintptrOften used withunsafe.PointerPlay with, used to do pointer operation, very cleverly

The last sentence, there is no special necessary words. It is not recommendedunsafeStandard library, it is not safe. Although it often makes you shine at the moment