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

huangapple go评论89阅读模式
英文:

Create array of C struct and pass struct pointer to C function

问题

我想为我的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)有什么区别?

谢谢。

英文:

I want to make a wrapper for my C function, which takes pointer to C struct as parameter.

In my Go code, I tried two approaches to allocate the space for 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))

For the (1) method, it works, but for (2) method, I got error message :

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

Why method (2) created type of *[0]byte instead of *C.struct_T32_Breakpoint, and it seems the cast to *C.struct_T32_Breakpoint doesn't work.

What's the difference of using method (1) and (2)?

Thanks.

Test Code

File: 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__ */

File: remote.c

#include "t32.h"

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

File : 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

得分: 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更强。

英文:

There are two reasons that the second snippet doesn't compile.

The first is that the capitalization doesn't match. The C declaration declares struct t32_breakpoint, but the Go code refers to struct T32_Breakpoint. C is case-sensitive, but it permits creating a pointer to a struct that has not been defined. So that is what CGo thinks you are doing. This pointer can't be dereferenced, since the contents and size of the struct are not known. It's basically equivalent to a void pointer, but with stronger typing. Cgo treats it as a void pointer, translating it as *[0]byte, i.e. a pointer to a zero-sized object. But unlike C, Go doesn't allow passing a void pointer to just any function that takes a pointer. So it has a type mismatch.

The other issue is that if you want to pass a struct t32_breakpoint* instead of a T32_Breakpoint*, you will need to change your function declaration in C. The distinction between the two types is probably not significant in C, but it is in Go, since Go has stronger typing than C.

huangapple
  • 本文由 发表于 2013年1月23日 13:25:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/14473032.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定