创建C结构数组并将结构指针传递给C函数

时间:2013-01-23 05:25:16

标签: go

我想为我的C函数创建一个包装器,它将指向C struct的指针作为参数。

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

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]字节的类型而不是* 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
}

1 个答案:

答案 0 :(得分:2)

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

首先是大写不匹配。 C声明声明了struct t32_breakpoint,但Go代码引用了struct T32_Breakpoint。 C区分大小写,但它允许创建指向尚未定义的结构的指针。这就是CGo认为你在做的事情。由于结构的内容和大小未知,因此无法取消引用此指针。它基本上等同于一个void指针,但键入更强。 Cgo将其视为void指针,将其转换为* [0]字节,即指向零大小对象的指针。但与C不同,Go不允许将void指针传递给任何带指针的函数。所以它的类型不匹配。

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