Golang类型不完整或不可分配问题探讨
Golang类型不完整或不可分配问题探讨
我正在编写一个C++包装器。
假设有以下项目:
src.cpp
#include "src.h"
struct SourceType {
int foo;
};
src.h:
struct SourceType;
c.h 是这个C++代码的C包装器:
typedef struct SourceType SourceType;
struct SourceType;
wrapper.h 是用于GO的包装器:
#pragma once
#include "c.h"
main.go:
package main
/*
#include "wrapper.h"
*/
import "C"
func main() {
var foo C.SourceType
_ = foo
}
我遇到了以下错误:
# example.com
./main.go:9:6: _Ctype_struct_SourceType is incomplete (or unallocatable); stack allocation disallowed
更多关于Golang类型不完整或不可分配问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
5 回复
哦,好的,明白了。我无法分配这个对象,我需要分配一个指针。
你好 @gucio321,
我在为一个C/C++项目创建Go绑定时遇到了完全相同的问题。能否请你解释一下你所说的“我无法分配这个对象,我需要分配一个指针。”具体是什么意思?我也很想了解如何在我的项目中解决这个问题。谢谢!
这是一个典型的Go与C/C++交互时遇到的类型不完整问题。错误表明Go编译器无法确定C.SourceType类型的大小,因此不能在栈上分配。
问题分析
在您的代码中:
- C头文件只声明了
SourceType的前向声明(struct SourceType;) - Go的cgo需要知道类型的完整定义才能确定其大小
- 前向声明对C/C++有效,但对Go的cgo来说类型是不完整的
解决方案
方案1:使用指针代替值类型(推荐)
package main
/*
#include "wrapper.h"
*/
import "C"
import "unsafe"
func main() {
// 使用指针而不是值类型
var foo *C.SourceType
// 如果需要分配内存,使用C.malloc
foo = (*C.SourceType)(C.malloc(C.size_t(unsafe.Sizeof(C.SourceType{}))))
defer C.free(unsafe.Pointer(foo))
// 或者通过C函数获取指针
// foo = C.createSourceType()
}
方案2:在C包装器中提供完整类型定义
修改c.h:
// c.h
#ifdef __cplusplus
extern "C" {
#endif
// 从C++源文件获取完整定义
typedef struct SourceType SourceType;
// 提供访问函数而不是暴露完整结构
SourceType* createSourceType();
void setFoo(SourceType* st, int value);
int getFoo(SourceType* st);
void destroySourceType(SourceType* st);
#ifdef __cplusplus
}
#endif
方案3:使用不透明指针模式
c.h:
// 不透明指针类型
typedef void* OpaqueSourceType;
OpaqueSourceType createSourceType();
void setFoo(OpaqueSourceType st, int value);
int getFoo(OpaqueSourceType st);
void destroySourceType(OpaqueSourceType st);
main.go:
package main
/*
#include "wrapper.h"
*/
import "C"
func main() {
// 使用不透明指针
var foo C.OpaqueSourceType = C.createSourceType()
defer C.destroySourceType(foo)
C.setFoo(foo, 42)
value := C.getFoo(foo)
_ = value
}
方案4:如果必须使用值类型,提供完整定义
修改c.h包含完整定义:
// c.h
#ifdef __cplusplus
extern "C" {
#endif
// 完整结构定义
typedef struct SourceType {
int foo;
} SourceType;
#ifdef __cplusplus
}
#endif
实际示例
这里是一个完整的工作示例:
src.h:
struct SourceType {
int foo;
};
c.h:
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SourceType {
int foo;
} SourceType;
SourceType* createSourceType();
void setFoo(SourceType* st, int value);
int getFoo(SourceType* st);
void destroySourceType(SourceType* st);
#ifdef __cplusplus
}
#endif
wrapper.h:
#pragma once
#include "c.h"
main.go:
package main
/*
#include "wrapper.h"
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
// 方法1:通过C函数创建
foo := C.createSourceType()
defer C.destroySourceType(foo)
C.setFoo(foo, 100)
fmt.Printf("Value: %d\n", C.getFoo(foo))
// 方法2:直接分配(需要完整类型定义)
var bar C.SourceType
// 通过unsafe.Pointer访问字段
*(*int32)(unsafe.Pointer(&bar.foo)) = 200
}
关键点
- Go的cgo需要知道C类型的完整大小才能在栈上分配
- 前向声明对cgo不够,需要完整定义
- 使用指针是更安全的方式,特别是当类型可能在不同平台有不同大小时
- 不透明指针模式是C/C++与Go交互的常用模式
对于C++包装器,推荐使用方案1或方案3,通过指针和访问函数来操作C++对象,这样可以更好地控制内存生命周期和保持类型安全。


