Golang中使用CGO与C++ vector交互的方法
Golang中使用CGO与C++ vector交互的方法 你好。 我猜这是一个C++问题,但我不知道在哪里发布我的问题,所以就在这里问了。
我尝试在cgo绑定中使用vector,但输出的第一个索引总是显示奇怪的数据,如下所示。
structs 是cgo二进制文件,company.exe 是cpp二进制文件。
$ ./structs
# 在声明vector的函数内部
0: wlrbbmqb, $6421.00
1: darzowk, $9172.00
2: hiddqs, $7862.00
3: xrjmowfr, $6393.00
4: jybldb, $8784.00
# 在声明vector的函数外部
#### 员工列表:
0: , $6421.00
1: darzowk, $9172.00
2: hiddqs, $7862.00
3: xrjmowfr, $6393.00
4: jybldb, $8784.00
$ ./company.exe
# 在声明vector的函数内部
0: wlrbbmqb, $6421.00
1: darzowk, $9172.00
2: hiddqs, $7862.00
3: xrjmowfr, $6393.00
4: jybldb, $8784.00
# 在声明vector的函数外部
#### 员工列表:
0: , $6421.00
1: darzowk, $9172.00
2: hiddqs, $7862.00
3: xrjmowfr, $6393.00
4: jybldb, $8784.00
我在Windows 11上尝试了msvc 2022 / mingw gcc 12.2.0,在ubuntu 20.04上尝试了gcc 9.4.0。 我是否遗漏了什么或者做错了什么?
任何提示都欢迎。
源代码在这里。
抱歉我的英语不好。
更多关于Golang中使用CGO与C++ vector交互的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢 @skillian,你的代码对我非常有帮助!
更多关于Golang中使用CGO与C++ vector交互的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您的回答。
也许是的,我也按照您提到的尝试了 emp_list_malloc,它运行良好。
尽管函数 generate_employee_list_vector 不工作,我猜测下面的重新声明会保留向量销毁后的值。
void generate_employee_list_vector(void* cmp, int count) {
....
c->emp_list = new employee_list{emp_list.data(), emp_list.size(), false};
}
我的疑问是,这是由于我的代码不成熟,还是C++编译器的问题。
你好,@edp1096,欢迎来到论坛!
我不是 C++ 程序员,所以我的理解可能不对,但我认为这可能与 vector 本身在 generate_employee_list_vector 函数结束时超出作用域有关。此时,vector(及其底层数组)会被销毁,因此当你稍后使用 emp_list 时,我认为你遇到的是未定义行为。我看到你还有另一个使用 malloc 的版本;你在那里也遇到了同样的情况吗?
不,数组的所有权并没有从向量转移到你的 employee_list。data 成员函数返回一个“非拥有”指针,该指针在向量存在期间可以使用。据我所知,无法将向量的后备数组移出向量。
我建议将向量放入 company 中,这样只要 company 存在,其生命周期就能得以保留。如果你想使用向量,我提交了一个拉取请求,其中包含了我建议的更改。我不能 100% 确定我做对了,但对我来说它是有效的。
这是一个典型的C++对象生命周期管理问题。当std::vector在C++函数中创建并返回指针时,函数结束后vector会被销毁,导致悬空指针。
以下是正确的交互方法:
1. C++头文件 (company.h)
#ifndef COMPANY_H
#define COMPANY_H
#include <vector>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
struct Employee {
char name[50];
double salary;
};
// 创建并返回vector指针
std::vector<Employee>* create_employee_vector();
// 获取vector大小
size_t get_vector_size(std::vector<Employee>* vec);
// 获取vector数据指针
Employee* get_vector_data(std::vector<Employee>* vec);
// 释放vector
void free_employee_vector(std::vector<Employee>* vec);
#ifdef __cplusplus
}
#endif
#endif
2. C++实现 (company.cpp)
#include "company.h"
#include <iostream>
#include <cstring>
extern "C" {
std::vector<Employee>* create_employee_vector() {
auto* vec = new std::vector<Employee>();
Employee emp1;
strncpy(emp1.name, "wlrbbmqb", sizeof(emp1.name));
emp1.salary = 6421.00;
vec->push_back(emp1);
Employee emp2;
strncpy(emp2.name, "darzowk", sizeof(emp2.name));
emp2.salary = 9172.00;
vec->push_back(emp2);
std::cout << "# 在声明vector的函数内部" << std::endl;
for (size_t i = 0; i < vec->size(); ++i) {
std::cout << " " << i << ": " << (*vec)[i].name
<< ", $" << (*vec)[i].salary << std::endl;
}
return vec;
}
size_t get_vector_size(std::vector<Employee>* vec) {
return vec ? vec->size() : 0;
}
Employee* get_vector_data(std::vector<Employee>* vec) {
return vec ? vec->data() : nullptr;
}
void free_employee_vector(std::vector<Employee>* vec) {
delete vec;
}
}
3. Go包装器 (main.go)
package main
/*
#cgo CXXFLAGS: -std=c++11
#cgo LDFLAGS: -lstdc++
#include "company.h"
*/
import "C"
import (
"fmt"
"unsafe"
)
type Employee struct {
Name string
Salary float64
}
func getEmployees() []Employee {
// 创建vector
vec := C.create_employee_vector()
defer C.free_employee_vector(vec)
// 获取vector信息
size := C.get_vector_size(vec)
data := C.get_vector_data(vec)
// 转换为Go切片
employees := make([]Employee, size)
// 使用unsafe.Pointer访问C数组
cArray := (*[1 << 30]C.struct_Employee)(unsafe.Pointer(data))[:size:size]
for i := 0; i < int(size); i++ {
emp := cArray[i]
employees[i] = Employee{
Name: C.GoString(&emp.name[0]),
Salary: float64(emp.salary),
}
}
return employees
}
func main() {
employees := getEmployees()
fmt.Println("#### 员工列表:")
for i, emp := range employees {
fmt.Printf(" %d: %s, $%.2f\n", i, emp.Name, emp.Salary)
}
}
4. 编译命令
# 编译C++代码
g++ -c -fPIC -std=c++11 company.cpp -o company.o
# 编译Go代码
go build -o structs
# 或者使用go build自动编译
CGO_ENABLED=1 CGO_CXXFLAGS="-std=c++11" go build -o structs
关键点:
- 使用
new在堆上分配vector,确保生命周期延续到Go端 - 通过
defer确保C++对象被正确释放 - 使用
vector::data()获取底层数组指针 - 使用
unsafe.Pointer进行类型转换 - 所有内存管理都在C++端完成,Go只负责调用和释放
这样就能正确地在Go和C++ std::vector之间传递数据,避免悬空指针问题。

