博客
关于我
C++调用Go方法的字符串传递问题及解决方案
阅读量:423 次
发布时间:2019-03-06

本文共 2975 字,大约阅读时间需要 9 分钟。

摘要:C++调用Go方法时,字符串参数的内存管理需要由Go侧进行深度值拷贝。

现象

在一个APP技术项目中,子进程按请求加载Go的ServiceModule,将需要拉起的ServiceModule信息传递给Go的Loader,存在C++调用Go方法,传递字符串的场景。

方案验证时,发现有奇怪的将std::string对象的内容传递给Go方法后,在Go方法协程中取到的值与预期不一致。

经过一段时间的分析和验证,终于理解问题产生的原因并给出解决方案,现分享如下。

背景知识

  1. Go有自己的内存回收GC机制,通过make等申请的内存不需要手动释放。
  2. C++中为std::string变量赋值新字符串后,.c_str()和.size()的结果会联动变化,尤其是.c_str()指向的地址也有可能变化。
  3. go build -buildmode=c-shared .生成的.h头文件中定义了C++中Go的变量类型的定义映射关系,比如GoString、GoInt等。其中GoString实际是一个结构体,包含一个字符指针和一个字符长度。

原理及解释

通过代码示例方式解释具体现象及原因,详见注释

C++侧代码:

//    // Created by w00526151 on 2020/11/5.    //         #include 
#include
#include
#include "libgoloader.h" /** * 构造GoString结构体对象 * @param p * @param n * @return */ GoString buildGoString(const char* p, size_t n){ //typedef struct { const char *p; ptrdiff_t n; } _GoString_; //typedef _GoString_ GoString; return {p, static_cast
(n)}; } int main(){ std::cout<<"test send string to go in C++"<

Go侧代码:

package main         import "C"    import (        "fmt"        "time"    )         func printInGo(p0 string, p1 string, p2 string){        time.Sleep(10 * time.Second)        fmt.Printf("in go function, p0:%s size %d, p1:%s size %d, p2:%s size %d", p0, len(p0), p1, len(p1), p2, len(p2))    }         //export LoadModule    func LoadModule(name string, version string, location string) int {        //通过make的方式,新构建一段内存来存放从C++处传入的字符串,深度拷贝防止C++中修改影响Go        tmp3rdParam := make([]byte, len(location))        copy(tmp3rdParam, location)        new3rdParam := string(tmp3rdParam)        fmt.Println("in go loadModule,first param is",name,"second param is",version, "third param is", new3rdParam)        go printInGo(name, version, new3rdParam);        return 0    }

Go侧代码通过-buildmode=c-shared的方式生成libgoloader.so及libgoloader.h供C++编译运行使用

go build -o libgoloader.so -buildmode=c-shared .

程序执行结果:

test send string to go in C++    in C++ tmpStr: 0x7fffe1fb93f0, tmpStr: /tmp/udsgateway-netconftemplateservice, tmpStr.size:38     # 将C++的指针传给Go,一开始打印都是OK的    in go loadModule,first param is /tmp/udsgateway-netconftemplateservice second param is /tmp/udsgateway-netconftemplateservice third param is /tmp/udsgateway-netconftemplateservice    # 在C++中,将指针指向的内容修改,或者删掉指针    in C++ change tmpStr and delete newStrPtr, new tmpStr: 0x7fffe1fb93f0, tmpStr: new string, tmpStr.size:10     # 在Go中,参数1、参数2对应的Go string变量都受到了影响,参数3由于做了深度拷贝,没有受到影响。    in go function, p0:new string eway-netconftemplateservice size 38, p1:        p���  netconftemplateservice size 38, p2:/tmp/udsgateway-netconftemplateservice size 38

结论

  • 结论:C++调用Go方法时,字符串参数的内存管理需要由Go侧进行深度值拷贝。即参数三的处理方式
  • 原因:传入的字符串GoString,实际是一个结构体,第一个成员p是一个char*指针,第二个成员n是一个int长度。

在C++代码中,任何对成员p的char*指针的操作,都将直接影响到Go中的string对象的值。

只有通过单独的内存空间开辟,进行独立内存管理,才可以避免C++中的指针操作对Go的影响。

ps:不在C++中进行内存申请释放的原因是C++无法感知Go中何时才能真的已经没有对象引用,无法找到合适的时间点进行内存释放。

本文分享自华为云社区《C++调用Go方法的字符串传递问题及解决方案》,原文作者:王芾。

 

转载地址:http://yvquz.baihongyu.com/

你可能感兴趣的文章
GC overhead limit exceeded
查看>>
mysql高可用
查看>>
17蓝桥试题之承压计算
查看>>
webservice 远程服务器返回错误:(400)错误的请求
查看>>
给JS对象添加扩展方法
查看>>
火焰纹章系列作历史
查看>>
bat中rar压缩命令
查看>>
[日常] PHP与Mysql测试kill慢查询并检验PDO的错误模式
查看>>
[日常] Go语言圣经-并发的非阻塞缓存
查看>>
[PHP] 工厂模式的日常使用
查看>>
[PHP] 控制反转依赖注入的日常使用
查看>>
[PHP] try catch在日常中的使用
查看>>
[Linux] 进程间通信
查看>>
[PHP] error_reporting(0)可以屏蔽Fatal error错误
查看>>
[PHP] 解决php中上传大文件的错误
查看>>
[Linux] 使用awk比较两个文件的内容
查看>>
[Git] 彻底删除github上的某个文件以及他的提交历史
查看>>
[Go] gin框架渲染html字符串
查看>>
[js] js中的闭包以及特点
查看>>
[操作系统]内存连续分配管理方式
查看>>