Golang low level programming: 1 Unsafe package

Time:2022-5-8

Go language ensures some safe properties in the design, and limits the way that the program may make mistakes. For example, strict type conversion rules. But it also makes many implementation details inaccessible through go programs, such as the memory layout of aggregate types (such as structures) or the machine code corresponding to a function.

Here we will discuss the unsafe package, which is implemented by the compiler to access the built-in features of the language. These features are generally invisible because they expose the go detailed memory layout. Although the name of the package is unsafe, these functions themselves are safe, and they are very helpful to understand the underlying memory layout of the function when doing memory optimization.

unsafe.Sizeof

unsafe. Sizeof reports the byte length of the parameter passed to it in memory. This parameter can be any type of expression. Sizeof will only report the byte length occupied by the memory of the fixed part of each data structure, such as the length occupied by the pointer or string, but will not report the indirect length of the string content, for example. For portability, the length of the reference type or the length containing the reference type is expressed in words. On the 32-bit system, the length of the word is 4 bytes, while on the 64 bit system, the length of the word is 8 bytes.

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var x struct{
		a bool
		b int16
		c []int
	}

	fmt.Println(unsafe.Sizeof(x.a))
	fmt.Println(unsafe.Sizeof(x.b))
	fmt.Println(unsafe.Sizeof(x.c))
	fmt.Println(unsafe.Sizeof(x))
}
//64 bit machine result:
//1: 1 byte, bool
//2: two bytes, 16 / 8 = 2
//24: slice 24 bytes and 3 words, because the slice contains a pointer, a length and a capacity.
//32: the first two plus memory empty bits are one word and eight bytes. So 8 + 24 = 32

If B and C exchange positions, the memory vacancy will be larger, 8 + 24 + 8 = 40

func main() {
	var x struct{
		a bool
		c []int
		b int16
	}

	fmt.Println(unsafe.Sizeof(x.a))
	fmt.Println(unsafe.Sizeof(x.c))
	fmt.Println(unsafe.Sizeof(x.b))
	fmt.Println(unsafe.Sizeof(x))
}
//64 bit machine result:
// 1
// 24
// 2
//40 here 8 + 24 = 32 is the same as our guess.

unsafe.Alignof

unsafe. Alignof reports the alignment required by the parameter type. This parameter can be any type of expression and returns a constant. Boolean and numeric types are aligned to their length (up to 8 bytes), and other types are aligned word by word.

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var x struct{
		a bool
		b int16
		c []int
	}

	fmt.Println(unsafe.Alignof(x.a))
	fmt.Println(unsafe.Alignof(x.b))
	fmt.Println(unsafe.Alignof(x.c))
	fmt.Println(unsafe.Alignof(x))
}

//64 bit machine result:
//1: This is boolean type. Boolean type and numeric type are aligned to their length
//2: the value type here is the same as above
//8: other types are word aligned. On 64 bit machines, a word is 8 bytes
// 8

unsafe.Offsetof(f)

Calculate the offset of member f relative to the starting address of the structure. If memory vacancies are also counted, the operand of the function must be a member selector: x.a.

func main() {
	var x struct{
		a bool
		c []int
		b int16
	}

	fmt.Println(unsafe.Offsetof(x.a))
	fmt.Println(unsafe.Offsetof(x.c))
	fmt.Println(unsafe.Offsetof(x.b))
}
//64 bit machine result:
//0: the first member of the structure
//8: there are 7 bytes of memory space here
// 32

unsafe.Pointer

unsafe. Pointer is a special type of pointer that can store the address of any variable. For an unsafe Because we don’t know the specific type of pointer, we can’t get its actual value indirectly through * P. Pointers of normal types can also be converted to unsafe Pointer of type pointer, unsafe Pointer of type pointer can be converted to pointer of normal type, and it does not have to be the same as the original type. Use unsafe Pointer type conversion can write arbitrary values to memory, thus destroying the type system.

Uintper type

The uintper type holds the value of the address pointed to by the pointer, so that numerical operations can be performed. (the uintpter type is an unsigned integer large enough to represent any address.) unsafe. Pointer can also be converted to uintptr. Of course, uintptr can also be converted to unsafe Pointer (this also breaks the type system).

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var x struct{
		a bool
		b int16
		c []int
	}

	//pb := &x.b
	pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))

	*pb = 42
	fmt.Println(x.b)
}

Here, first convert the address of X to unsafe The pointer type is thus converted to the uintptr type, which can be used for calculation. After calculation, it is converted to the original type and assigned a value to the area pointed to by the memory address. However, temporary variables of uintptr type cannot be introduced here, such as the following. Because the garbage collector will move variables in memory, in order to reduce memory fragmentation. However, in the garbage collector, the uintptr type is only a value, so the data in the memory corresponding to the pointer stored in uintptr will not be changed after moving.

ptr := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
    pb := (*int16)(unsafe.Pointer(ptr))

summary

The unsafe package can be used to manipulate memory, thus increasing the flexibility of go language, but the unsafe package cannot guarantee compatibility in the future go language upgrade. The uintptr type cannot be used as a temporary variable. And convert the uintptr type to unsafe In the process of pointer, the number of uintptr operations should be minimized.

Recommended Today

Typical example of Vue two field joint verification — changing password

1. Foreword   this article is the foregoing《Vue element UI form verification rules, what do you master?》For the typical application of multi field joint verification. When changing the password, you generally need to confirm that the password is the same twice, involving two attribute fields. Similar cases involving two attribute fields are: Date time range. […]