The data type conversion involved in CGO includes the following:

  • Numeric type
  • String and slice types
  • Structure, union, enumeration type'
  • array type
  • pointer type
  • Converting between arrays and pointers
  • Conversion between slices and slices

We have sorted out the first three in the previous short article, and we will continue

array type

In C language:

  • array

In the C language, the array name corresponds to a pointer, pointing to a specific type of memory of a specific length, but this pointer cannot be modified

A string in C language is an array of char type. The length of the string needs to be determined according to the position of the NULL character representing the end.

  • string

is an array of type char

  • slice

C language has no concept of slice

In the GO language:

  • array

An array is a value type, and the length of the array is a part of the array type

  • string

It is a read-only byte type memory of a certain length.

  • slice

is a simple dynamic array

From the above, we can see that the mutual conversion of arrays, slices, and strings in C language and GO language can be the conversion of pointers and the length of memory pointed to by pointers.

CGO officially provides us with the following 5 functions for the conversion between C language and GO language:

  • func C.CString(string) *C.char

C.CString Clone the incoming go string into a C-formatted string. The cloned string is created by malloc in C language, so we run out of this function and need to release it manually Memory

  • func C.CBytes([]byte) unsafe.Pointer

C.CBytes is used to clone and convert the input go byte type array (slice) into C language pointer. The pointer is an array, which needs to open up space. When not in use, it also needs to be released manually.

  • func C.GoString(*C.char) string

C.GoString Clone C string into GO string, GO itself will release memory

  • func C.GoStringN(*C.char, C.int) string

C.GoStringN , convert a string of a certain length in C into a string of GO, and GO will release memory by itself

  • func C.GoBytes(unsafe.Pointer, C.int) []byte

C.GoBytes Convert C arrays into GO slices

summary:

The above-mentioned set of officially provided functions, the conversion between GO language and C language are realized by cloning

GO to C

C uses malloc to open up memory in C's own space, so when we don't need to use it, we need to release it

C to GO

It is also through cloning, but GO has its own memory management, which does not require us to manually release memory

Advantages of the above functions

  • The conversion is performed by cloning, the memory is opened up in the respective language, and the memory management is simple

Disadvantages of the above functions

  • Mutual conversion requires additional memory, which increases system overhead

pointer-to-pointer conversion

In cgo, how to realize the conversion between pointers and pointers?

In C language:

Pointers are the soul of the C language. In the C language, coercion can be performed between pointers of different types, and free conversion between pointers is also the first important problem to be solved in cgo code.

In the GO language:

The different types of conversions are very strict, and any warning message that might appear in C might be an error in Go.

Different types of pointers in GO are forbidden to be converted, but with CGO, this imprisonment is broken, and the unsafe.pointer pointer type can be used as a bridge to convert

E.g

 var a *A
var b *B

b = (*b)(unsafe.Pointer(a)) // *A => *B
a = (*a)(unsafe.Pointer(b)) // *B => *A

Conversion between numbers and pointers

In cgo, how to realize the conversion of values and pointers?

In the GO language:

Direct conversion of numeric types to pointer types is prohibited

But with cgo, we have a way. Go unsafe.Pointer pointer type defines a uintptr type , we still use them as a bridge to convert into our target pointer

For example, how do we convert the value of int32 in a GO to a pointer in C?

As mentioned above, we make good use of this bridge to convert int32 into uintptr, then into unsafe.pointer, and finally into C char pointer

Conversion between slices and slices

In cgo, how to realize the conversion between slices and slices?

The conversion between slices and slices uses the data structure provided by the reflect package in GO.

Because in GO, the array or slice is no longer a pointer type, and it needs to be converted by using the data structure in reflect, as follows:

 //它的引用不会被垃圾回收,因此程序必须使用正确类型的指向底层数据的指针
type StringHeader struct {
    Data uintptr
    Len  int
}

//它的引用不会被垃圾回收,因此程序必须使用正确类型的指向底层数据的指针
type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

How to convert slice A into slice B?

We can do the conversion like this, first constructing an empty slice

 var p []int
var q []string

pk := (*reflect.SliceHeader)(unsafe.Pointer(&p))
qk := (*reflect.SliceHeader)(unsafe.Pointer(&q))

Then use the original data to fill this empty slice, here you need to pay attention to the calculation and assignment of len and cap

 pk.Data = qk.Data
pk.Len = qk.Len * unsafe.Sizeof(q[0]) / unsafe.Sizeof(p[0])
pk.Cap = qk.Cap * unsafe.Sizeof(q[0]) / unsafe.Sizeof(p[0])

function call

The basic function call we have demonstrated in the last short article, let's look at other

  • How the return value of the C function itself is used in GO

How the return value of the C function itself is used in GO

Let's write a C function with a return value, and then call GO again:

The C language does not support multiple return results, but the GO language supports returning one result. In CGO, we can use the <errno.h> errno macro in the standard library to return the error status

 package main

/*
#include <errno.h>

static int testadd(int a, int b) {
    if (a < 0){
        errno = EINVAL;
        return 0;
    }
    return a+b;
}
*/
import "C"
import "fmt"

func main() {
    v0, err0 := C.testadd(-2, 1)
    fmt.Println(v0, err0)

    v1, err1 := C.testadd(1, 2)
    fmt.Println(v1, err1)
}

The execution result is as follows:

 0 invalid argument
3 <nil>

If the C function is no return value, we can also deal with this.

For example it could be

 v, _ := C.xxx()
fmt.Printf("%#v", v)    // 输出为 main._Ctype_void{}
fmt.Println(C.xxx())    // 输出为 [] 0长的数组类型[0]byte

After we actually practiced it, we found that the void type of the C language corresponds to the _Ctype_void type in the current main package

A drop of water wears a stone, learn step by step

References:

GO Advanced Programming

Welcome to like, follow, favorite

Friends, your support and encouragement are the motivation for me to persist in sharing and improving quality

Okay, here it is this time

Technology is open, and our mentality should be open. Embrace change, live in the sun, and move forward.

I am the native of Abingyun , welcome to like, follow and collect, see you next time~


阿兵云原生
192 声望37 粉丝