首页 > 解决方案 > how to copy C.int address into C.char in cgo?

问题描述

using cgo I am calling c function. I ran into situation where I have to copy C.int address into C.char[4]. Can I do that in go?

code snippet C- Structure:

struct data
{
    char    *a_add; 
    unsigned int length;
}

Go-Code

func main() {
     var d[3] C.data
     var filedescriptor C.int
     d[0].a_add = &filedescriptor 
     d[0].length = 4
}

The problem is a_add is a char*. But I need to pass int variable address. The c-code is a legacy code, and I can't fix datatype now. Other C modules uses it, and it's working in C with a warning. where as in go, it is error.

Is there any way that I can copy address of int variable into char* array in cgo.

Update: I tried d[0].a_add = (*C.char)(unsafe.Pointer(&filedescriptor )),

getting Error: panic: runtime error: cgo argument has Go pointer to Go pointer

What Am I missing?

标签: cgocgo

解决方案


One of the problems you are running into is that in a call into C code, you may not pass a pointer to a Go pointer. The variable filedescriptor is a C.int, but &filedescriptor is a Go pointer, so you cannot use that (or rather, you cannot use that in the a_add field as a value).

There is a great deal about your C code that is not clear to me, but you can probably use the code below. Note that this code may be overkill for your particular situation. It is not meant to be particularly efficient or good, just as clear as possible while being extremely flexible—for instance, it can read from and write to packed C structures.

package main

// #include <stdio.h>
// #include <stdlib.h>
// #include <string.h>
//
// struct data {
//     char *a_add;
//     unsigned int length;
// };
//
// void f(struct data *p) {
//    printf("p->a_add = %p, p->length = %u\n", p->a_add, p->length);
//    printf("p->a_add as an int: %d\n", *(int *)p->a_add);
//    *(int *)p->a_add = 0x12345678;
// }
import "C"

import (
        "fmt"
        "unsafe"
)

const cIntSize = C.sizeof_int

// Produce a Go int64 from a C int.  The caller passes the address
// of the C int.
func int64FromCInt(ci unsafe.Pointer) int64 {
        // Get a slice pointing to the bytes of the C int.
        sci := (*[cIntSize]byte)(ci)[:]
        switch {
        case cIntSize == unsafe.Sizeof(int64(0)):
                var gi int64
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
                copy(sgi, sci)
                return gi
        case cIntSize == unsafe.Sizeof(int32(0)):
                var gi int32
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
                copy(sgi, sci)
                return int64(gi)
        case cIntSize == unsafe.Sizeof(int(0)):
                var gi int
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
                copy(sgi, sci)
                return int64(gi)
        default:
                panic("no Go integer size matches C integer size")
        }
}

// Write C int (via an unsafe.Pointer) from Go int.  The caller
// passes the address of the C int.
func writeCIntFromInt(gi int, ci unsafe.Pointer) {
        // Get a slices covering the bytes of the C int.
        sci := (*[cIntSize]byte)(ci)[:]
        switch {
        case cIntSize == unsafe.Sizeof(gi):
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
                copy(sci, sgi)
        case cIntSize == unsafe.Sizeof(int64(0)):
                // Copy value to int64 for copying purposes.
                // Since int64 holds all int values, this always works.
                gi2 := int64(gi)
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
                copy(sci, sgi)
        case cIntSize == unsafe.Sizeof(int32(0)):
                // Copy value to int32 for copying purposes.
                // Panic if we destroy the value via truncation.
                gi2 := int32(gi)
                if int(gi2) != gi {
                        panic(fmt.Sprintf("unable to send Go value %x to C: size of Go int=%d, size of C int=%d", gi, unsafe.Sizeof(gi), cIntSize))
                }
                sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
                copy(sci, sgi)
        default:
                panic("no Go integer size matches C integer size")
        }
}

func main() {
        b := C.malloc(cIntSize)
        defer C.free(b)
        writeCIntFromInt(32767, b)
        d := C.struct_data{a_add: (*C.char)(b), length: cIntSize}
        fmt.Println("calling C.f(d)")
        C.f(&d)
        result := int64FromCInt(unsafe.Pointer(d.a_add))
        fmt.Printf("result = %#x\n", result)
}

推荐阅读