Golang中CGO返回CString类型给C++时结果不正确的问题
Golang中CGO返回CString类型给C++时结果不正确的问题 我使用TiDB(一个键值结构数据库引擎),通过CGO将其接口制作成共享库,然后通过C++调用。当我写入一段数据并查找该数据时,发生了奇怪的现象。
我可以在共享库中打印出这段数据,无论是字节类型还是字符串类型。但当我将其传回C++时,数据看起来被截断了。
C++中的代码块如下:
std::string key = "f590ca8a-0283-4389-a769-5d66b8be72be.546733.1_wlf1";
char *tarKey = const_cast<char*>(key.c_str());
int newStrSize = key.size();
cout<<"start get"<<endl;
GetKey_return getRes = ((FUNC_GETKEY)funcMap["GetKey"])(1, beginRes, buildGoString(tarKey, newStrSize), 0);
std::cout<<"res is " << getRes.r1 << std::endl;// 这里什么都打印不出来!!!!!!!!!
struct GetKey_return {
GoInt32 r0;
char* r1;
};
由GO编译的共享库中的代码如下:
val, err := txn.Get(context.TODO(), []byte(key))
if err != nil {
if use == 0{
defer CloseTxn(client, index)
}
none := C.CString("")
defer C.free(unsafe.Pointer(none))
if err == kv.ErrNotExist{
return ErrorKeyNotExist, none
}
return ErrorGetKeyFail, none
}
// todo: 0 close,1 continue use
if use == 0{
defer CloseTxn(client, index)
}
fmt.Println(val) // Val 是字节类型,可以正常打印。我给出的结果如下。
sval := string(val)
fmt.Println(sval) // 这里也可以打印。我给出的结果如下。
res := C.CString(sval)
fmt.Println(res)
defer C.free(unsafe.Pointer(res))
return OperatorSuccess, res
这个字节的值是:
[11 3 233 2 0 0 1 1 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 8 3 97 0 0 0 1 173 71 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 0 0 0 51 52 54 53 51 100 102 99 54 53 99 52 57 99 99 97 48 102 102 56 54 97 100 101 99 49 97 101 98 54 54 48 2 0 0 0 108 113 173 71 0 0 0 0 0 0 6 0 0 0 109 97 115 116 101 114 8 0 0 0 83 84 65 78 68 65 82 68 0 0 0 0 0 0 0 0 45 0 0 0 102 53 57 48 99 97 56 97 45 48 50 56 51 45 52 51 56 57 45 97 55 54 57 45 53 100 54 54 98 56 98 101 55 50 98 101 46 54 54 50 48 57 53 46 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 12 0 0 0 117 115 101 114 46 114 103 119 46 97 99 108 113 0 0 0 3 2 107 0 0 0 3 2 12 0 0 0 2 0 0 0 108 113 2 0 0 0 108 113 3 3 79 0 0 0 1 1 0 0 0 2 0 0 0 108 113 15 0 0 0 1 0 0 0 2 0 0 0 108 113 4 3 44 0 0 0 2 2 4 0 0 0 0 0 0 0 2 0 0 0 108 113 0 0 0 0 0 0 0 0 2 2 4 0 0 0 15 0 0 0 2 0 0 0 108 113 0 0 0 0 0 0 0 0 0 0 0 0 21 0 0 0 117 115 101 114 46 114 103 119 46 99 111 110 116 101 110 116 95 116 121 112 101 1 0 0 0 0 13 0 0 0 117 115 101 114 46 114 103 119 46 101 116 97 103 33 0 0 0 51 52 54 53 51 100 102 99 54 53 99 52 57 99 99 97 48 102 102 56 54 97 100 101 99 49 97 101 98 54 54 48 0 17 0 0 0 117 115 101 114 46 114 103 119 46 109 97 110 105 102 101 115 116 35 1 0 0 6 3 29 1 0 0 173 71 0 0 0 0 0 0 36 0 0 0 119 108 102 49 46 121 120 77 85 109 105 74 86 53 121 79 105 84 105 122 110 86 48 111 77 97 98 45 107 105 76 68 118 71 122 105 1 0 0 0 0 0 0 0 0 0 0 0 2 1 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 64 0 0 0 0 0 0 0 0 0 45 0 0 0 102 53 57 48 99 97 56 97 45 48 50 56 51 45 52 51 56 57 45 97 55 54 57 45 53 100 54 54 98 56 98 101 55 50 98 101 46 53 52 54 55 51 51 46 49 1 1 132 0 0 0 36 0 0 0 53 100 52 56 55 102 52 56 45 57 100 98 99 45 49 49 101 98 45 57 98 48 56 45 49 52 49 56 55 55 53 50 57 100 97 100 36 0 0 0 49 99 53 50 50 102 99 48 45 57 100 98 99 45 49 49 101 98 45 98 49 53 98 45 49 52 49 56 55 55 53 50 57 100 97 100 0 0 0 0 36 0 0 0 53 100 52 56 55 102 52 56 45 57 100 98 99 45 49 49 101 98 45 57 98 48 56 45 49 52 49 56 55 55 53 50 57 100 97 100 0 0 0 0 0 0 0 0 0 1 1 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 将上述字节转换为字符串后,输出为:
aG 34653dfc65c49cca0ff86adec1aeb660lqGmasteSTANDARD-f590ca8a-0283-4389-a769-5d66b8be72be.662095.2 user.rgw.aclqk user.rgw.etag!34653dfc65c49cca0ff86adec1aeb660user.rgw.manifest#G$wlf1.yxMUmiJV5yOiTiznV0oMab-kiLDvGzi @-f590ca8a-0283-4389-a769-5d66b8be72be.546733.1$5d487f48-9dbc-11eb-9b08-141877529dad$1c522fc0-9dbc-11eb-b15b-141877529dad$5d487f48-9dbc-11eb-9b08-141877529dad
将字符串返回给C++后,什么都打印不出来。
更多关于Golang中CGO返回CString类型给C++时结果不正确的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你遇到了什么错误?我不确定这是否是问题所在,但看起来你的Go代码释放了它返回的字符串,这很可能会导致释放后使用错误。
更多关于Golang中CGO返回CString类型给C++时结果不正确的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是的,这是问题之一,但即使我删除了 defer 函数,这个问题仍然存在。如您所见,这个字节切片长度为 751 个字符,但当我将其返回到 C++ 时,它只有 4 个字符长。 因此,我认为内部的 CString 函数可能导致字节切片被截断。
C字符串(至少是由C.CString返回的那些)在第一个零字节处终止。它们不像Go字符串那样是长度分隔的。
如果你的字符串要包含嵌入的空字符,你将需要使用另一种字节序列类型,例如由C.CBytes返回的类型。
(我认为C.CString仍然可以工作,但你需要将长度也传递给C++,并从中创建一个std::string或类似的东西。)
func main() {
fmt.Println("hello world")
}
问题出在CGO返回的C字符串包含空字符(\0)。当使用C.CString()转换包含空字节的Go字节切片时,C字符串会在第一个空字符处被截断。C++的std::cout在遇到空字符时会停止输出,导致看起来像是空字符串。
以下是修复方案:
// 返回字节切片和长度给C++
type GetKey_return struct {
r0 C.int
r1 unsafe.Pointer // 改为指针类型
r2 C.size_t // 添加长度字段
}
// Go端实现
func GetKeyWrapper(key string) (C.int, unsafe.Pointer, C.size_t) {
val, err := txn.Get(context.TODO(), []byte(key))
if err != nil {
if err == kv.ErrNotExist {
return C.int(ErrorKeyNotExist), nil, 0
}
return C.int(ErrorGetKeyFail), nil, 0
}
// 分配内存并复制数据
size := C.size_t(len(val))
ptr := C.malloc(size)
if ptr == nil {
panic("C.malloc failed")
}
C.memcpy(ptr, unsafe.Pointer(&val[0]), size)
// 注意:需要在C++端释放这个内存
return C.int(OperatorSuccess), ptr, size
}
C++端修改:
struct GetKey_return {
int32_t r0;
void* r1; // 改为void指针
size_t r2; // 添加长度字段
};
// 使用示例
GetKey_return getRes = ((FUNC_GETKEY)funcMap["GetKey"])(1, beginRes, buildGoString(tarKey, newStrSize), 0);
if (getRes.r1 != nullptr) {
// 将数据转换为字符串(包含所有字节)
std::string result(static_cast<char*>(getRes.r1), getRes.r2);
std::cout << "res length: " << result.length() << std::endl;
std::cout << "res data: " << result << std::endl;
// 释放Go端分配的内存
free(getRes.r1);
}
或者使用更简单的方案,通过base64编码:
import "encoding/base64"
func GetKeyBase64(key string) (C.int, *C.char) {
val, err := txn.Get(context.TODO(), []byte(key))
if err != nil {
return C.int(ErrorGetKeyFail), C.CString("")
}
// 使用base64编码避免空字符问题
encoded := base64.StdEncoding.EncodeToString(val)
return C.int(OperatorSuccess), C.CString(encoded)
}
C++端解码:
GetKey_return getRes = ((FUNC_GETKEY)funcMap["GetKey"])(...);
std::string encoded(getRes.r1);
std::string decoded = base64_decode(encoded);
std::cout << "decoded data: " << decoded << std::endl;
关键点是:二进制数据包含空字符(0x00),不能直接作为C字符串传递。需要传递长度信息或使用编码方案。

