头图

Pointer type conversion

In the Go language, for safety reasons, conversion of two pointer types is not allowed. For example, int cannot be converted to float64.

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

operation result:

cannot convert ip (type * int) to type * float64

It is found that an error is reported, and the forced transformation cannot be carried out.
~ If you have to, then the Go provides unsafe package, use the bag Pointer to convert!

unsafe.Pointer

unsafe.Pointer is a pointer with a special meaning, which can represent any type of address. Use it to convert between two pointer types.

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

In the example, we can do any conversion between different pointer types through unsafe.Pointer. Let's look at the source definition of 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
  • ArbitraryType can represent any type, we don’t need to pay much attention to ArbitraryType, know that it can represent any type
  • unsafe.Pointer is *ArbitraryType, indicating that unsafe.Pointer is a pointer of any type, a universal pointer, and can represent any memory address.

uintptr pointer type

uintptr is also a pointer type and can also represent any type. The difference from is that 16104aeaa33d99 uintptr can perform operations . The pointer offset can be calculated through uintptr to achieve the purpose of accessing specific memory and reading and writing to specific memory.
Example:

func main() {
   p := new(person)
   //Name是person的第一个字段不用偏移,即可通过指针修改
   pName := (*string)(unsafe.Pointer(p))
   *pName = "微客鸟窝"
   //Age并不是person的第一个字段,所以需要进行偏移,这样才能正确定位到Age字段这块内存,才可以正确的修改
   pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Offsetof(p.Age)))
   *pAge = 20
   fmt.Println(*p)
}
type person struct {
   Name string
   Age int
}
//运行结果:
{微客鸟窝 20}

Analysis:

  1. First use the new function to declare a pointer variable p of type *person.
  2. Then the pointer variable p of type person is converted to the pointer variable pName of type
  3. Because the first field of the person structure is the name of the string type, the pointer pName points to the Name field (offset 0). Modifying pName is actually modifying the value of the field Name.
  4. Because the Age field is not the first field of person, pointer offset operations must be performed to modify it. Therefore, the pointer variable p needs to be converted to uintptr through unsafe.Pointer first, so that the address operation can be performed. How much is the specific offset? It can be calculated by the function unsafe.Offsetof, which returns a uintptr type offset. With this offset, the correct memory address of the Age field can be obtained through the + operator, that is, through unsafe. Pointer converted pointer variable pAge of type *int.
  5. If you want to perform pointer arithmetic, you must first convert it to a uintptr type pointer through unsafe.Pointer. After the pointer calculation is completed, it must be converted to a real pointer type by unsafe.Pointer, so that this memory can be assigned or fetched.
  6. With the pointer variable pAge that points to the field Age, you can assign a value to it and modify the value of the field Age.
  7. The example is just to demonstrate the pointer arithmetic of uintptr, the normal encoding structure assignment is not so complicated:

    p :=new(person)
    p.Name = "微客鸟窝"
    p.Age = 20
    fmt.Println(*p)
    } 

Pointer conversion rules

Three pointer types: *T, unsafe.Pointer, unitptr

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

  • unsafe.Pointer is mainly used for the conversion of pointer types and is a bridge for the conversion of various pointer types.
  • uintptr is mainly used for pointer arithmetic, especially to locate different memory by offset.

unsafe.Sizeof

The Sizeof function can return the size of the memory occupied by a type. This size is only related to the type and has nothing to do with the size of the data stored in the variable corresponding to the type. For example, the bool type occupies one byte, and int8 also occupies one byte.
The Sizeof function can be used to view the amount of memory occupied by any type, for example:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    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("微客鸟窝")))
    fmt.Println(unsafe.Sizeof([]string{"有码无尘","无尘"}))
}
  • For integer types, the number of bytes occupied means the size of the range of numbers stored in this type. For example, int8 occupies one byte, which is 8bit, so the size range it can store is -128~~127, which is −2^( n-1) to 2^(n-1)−1. Among them, n means bit, int8 means 8bit, int16 means 16bit, and so on.
  • For the int type related to the platform, depending on whether the platform is 32-bit or 64-bit, the largest will be taken.
  • The memory footprint of a struct structure is equal to the sum of the memory footprint of the field types it contains.

微客鸟窝
37 声望3 粉丝