Pointer conversion

In Go, conversions between two pointer types are not allowed for security reasons. For example, *int cannot be converted to *float64.

func main(a) {
    i := 5
    ip := &i
    var fp *float64 = (*float64)(ip)
}
Copy the code

Running result:

cannot convert ip (type * int) to type * float64
Copy the code

An error has been reported and the forced transformation cannot be carried out. If you must, then Go provides the unsafe package, using Pointers in the package to do the transformation!

unsafe.Pointer

Unsafe.pointer is a special Pointer that can represent an address of any type. You can use it to convert two pointer types.

func main(a) {
   i:= 5
   ip := &i
   var fp *float64 = (*float64)(unsafe.Pointer(ip))
   *fp = *fp * 6
   fmt.Println(i)
}
Copy the code

In our example, we can do any conversion between Pointer types with unsafe.pointer. Let’s look at the source definition for unsafe.pointer:

// ArbitraryType is here for the purposes of documentation
// only and is not actually part of the unsafe package. 
// It represents the type of an arbitrary Go expression.
type ArbitraryType int
type Pointer *ArbitraryType
Copy the code
  • A ArbitraryType can be any type, so we don’t have to pay much attention to a ArbitraryType to know that it can be any type
  • Unsafe. Pointer is *ArbitraryType, indicating that unsafe.Pointer is a Pointer of any type. It is a generic Pointer that can represent any memory address.

Uintptr pointer type

Uintptr is also a Pointer type and it can represent anything. The difference between it and unsafe.pointer is that he uintPtr can operate. The uintPtr can be used to calculate the pointer offset to access specific memory and read and write to specific memory. Example:

func main(a) {
   p := new(person)
   //Name is the first field in person
   pName := (*string)(unsafe.Pointer(p))
   *pName = "A little bird's nest."
   // The Age field is not the first field in person, so you need to offset it so that you can correctly locate the Age field and modify it correctly
   pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Offsetof(p.Age)))
   *pAge = 20
   fmt.Println(*p)
}
type person struct {
   Name string
   Age int
}
// Run result:{guest bird's nest20}
Copy the code

Resolution:

  1. Use the new function to declare a pointer variable p of type *person.
  2. Unsafe. Pointer (p); / / Unsafe. Pointer (p); / / unsafe.Pointer (pName);
  3. Since the first field of the person structure is the string Name, the pName pointer points to the Name field (offset 0), and modifying pName is modifying the value of the Name field.
  4. Because the Age field is not the first field in person, a pointer offset operation is required to modify it. Unsafe.pointer needs to be translated to uintptr from p to address. How much is the offset? The unsafe.offsetof function returns a uintPtr offset. Using this offset, you can obtain the correct Age field address using the + operator. Unsafe.pointer is an *int variable called pAge.
  5. Unsafe.pointer is translated to uintPtr using an unsafe.pointer. Unsafe.pointer is converted to the actual Pointer type via unsafe.pointer, so you can assign or value the piece of memory.
  6. Now that we have a pointer variable pAge to the Age field, we can assign to it and change the value of the Age field.
  7. The example is just to demonstrate uintPtr pointer operation. Normal coding structure assignment is much less complicated:
P :=new(person) p.name = 20 FMT.Println(*p)}Copy the code

Pointer conversion rule

There are three Pointer types: *T, unsafe.Pointer, and unitptr

*T ← interturn → unsafe.Pointer ← interturn → unitptr

  • Unsafe.pointer Is used to convert Pointer types. It Bridges the various Pointer types.
  • Uintptr is primarily used for pointer arithmetic, especially to locate different types of memory by offset.

unsafe.Sizeof

Sizeof returns the Sizeof the memory used by a type. This size is dependent only on the type and is independent of the Sizeof the data stored in the corresponding variable, such as bool and int8. We can use Sizeof to see the Sizeof any type of memory. For example:

package main

import (
	"fmt"
	"unsafe"
)

func main(a) {
	fmt.Println(unsafe.Sizeof(true)) / / 1
	fmt.Println(unsafe.Sizeof(int8(0 / / 1
	fmt.Println(unsafe.Sizeof(int16(10))) / / 2
	fmt.Println(unsafe.Sizeof(int32(10000000))) / / 2
	fmt.Println(unsafe.Sizeof(int64(10000000000000))) / / 1
	fmt.Println(unsafe.Sizeof(int(10000000000000000)))
	fmt.Println(unsafe.Sizeof(string("A little bird's nest.")))
	fmt.Println(unsafe.Sizeof([]string{"Code without dust."."Clean"}}))Copy the code
  • For integers, the number of bytes used means the size of the range of numbers that the type stores. For example, int8 takes one byte, which is 8 bits, so it can store sizes ranging from -128 to 127, which is −2^(n-1) to 2^(n-1)−1. N indicates the bit, int8 indicates 8 bits, int16 indicates 16 bits, and so on.

  • For platform-specific ints, whichever is the largest, depending on whether the platform is 32-bit or 64-bit.

  • The memory footprint of a struct is equal to the sum of the memory footprint of the field types it contains.