3

我想为我的 C 函数制作一个包装器,它将指向 C 结构的指针作为参数。

在我的 Go 代码中,我尝试了两种方法来为 C 结构分配空间:

bps := make([]_Ctype_T32_Breakpoint, max)          (1)
C.MyFunc((*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))

bps := make([]C.struct_T32_Breakpoint, max)        (2)
C.MyFunc((*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))

对于 (1) 方法,它可以工作,但对于 (2) 方法,我收到错误消息:

cannot use (*[0]byte)(unsafe.Pointer(&bps[0])) (type *[0]byte) as type *_Ctype_T32_Breakpoint in function argument

为什么方法 (2) 创建了 *[0]byte 类型而不是 *C.struct_T32_Breakpoint,并且似乎对 *C.struct_T32_Breakpoint 的强制转换不起作用。

使用方法(1)和(2)有什么区别?

谢谢。

测试代码

文件:t32.h

#ifndef __T32_H__
#define __T32_H__

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;

typedef struct t32_breakpoint {
    dword address;
    byte  enabled;
    dword type;
    dword auxtype;
} T32_Breakpoint;


int T32_GetBreakpointList( int *, T32_Breakpoint*, int );

#endif /* __T32_H__ */

文件:remote.c

#include "t32.h"

int T32_GetBreakpointList (int* numbps, T32_Breakpoint* bps, int max)
{ 
    return 0;
}

文件:t32.go

package t32

// #cgo linux,amd64 CFLAGS: -DT32HOST_LINUX_X64
// #cgo linux,386 CFLAGS: -DT32HOST_LINUX_X86
// #cgo windows,amd64 CFLAGS: -D_WIN64
// #cgo windows,386 CFLAGS: -D_WIN32
// #cgo windows CFLAGS: -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
// #cgo windows LDFLAGS: -lkernel32 -luser32 -lwsock32
// #include "t32.h"
// #include <stdlib.h>
import "C"
import (
    "errors"
    "unsafe"
)

const (
    _INVALID_U64 = 0xFFFFFFFFFFFFFFFF
    _INVALID_S64 = -1
    _INVALID_U32 = 0xFFFFFFFF
    _INVALID_S32 = -1
    _INVALID_U16 = 0xFFFF
    _INVALID_S16 = -1
    _INVALID_U8  = 0xFF
    _INVALID_S8  = -1
)

type BreakPoint struct {
    Address uint32
    Enabled int8
    Type    uint32
    Auxtype uint32
}

func GetBreakpointList(max int) (int32, []BreakPoint, error) {
    var numbps int32

    // bps := make([]_Ctype_T32_Breakpoint, max)                     // Method (1), can compile
    // code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))

    bps := make([]C.struct_T32_Breakpoint, max)                        // Method (2) can't compile
    code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))

    if err != nil {
        return _INVALID_S32, nil, err
    } else if code != 0 {
        return _INVALID_S32, nil, errors.New("T32_GetBreakpointList Error")
    }
    if numbps > 0 {
        var gbps = make([]BreakPoint, numbps)
        for i := 0; i < int(numbps); i++ {
            gbps[i].Address = uint32(bps[i].address)
            gbps[i].Auxtype = uint32(bps[i].auxtype)
            gbps[i].Enabled = int8(bps[i].enabled)
            gbps[i].Type = uint32(bps[i]._type)
        }
        return numbps, gbps, nil
    }
    return 0, nil, nil
}
4

1 回答 1

2

第二个片段无法编译有两个原因。

首先是大小写不匹配。C 声明声明了 struct t32_breakpoint,但 Go 代码引用了 struct T32_Breakpoint。C 区分大小写,但它允许创建指向尚未定义的结构的指针。所以这就是 CGo 认为你正在做的事情。这个指针不能被取消引用,因为结构的内容和大小是未知的。它基本上相当于一个 void 指针,但具有更强的类型。Cgo 把它当作一个 void 指针,把它翻译成 *[0]byte,即一个指向零大小对象的指针。但与 C 不同的是,Go 不允许将 void 指针传递给任何接受指针的函数。所以它有一个类型不匹配。

另一个问题是,如果你想传递一个 struct t32_breakpoint* 而不是 T32_Breakpoint*,你需要在 C 中更改你的函数声明。这两种类型之间的区别在 C 中可能并不重要,但在 Go 中,因为 Go 的类型比 C 强。

于 2013-01-24T20:17:35.400 回答