从C#调用C++库遇偶发崩溃:临时对象生命周期是否合法?
问题描述
我在从C#调用一个不受我控制的C库时遇到了偶发崩溃问题。该C库的内部代码大致如下:
#include <string> #include <memory> class Holder { public: static std::shared_ptr<Holder> create() { return std::make_shared<Holder>(); } const std::string& getRef() { return value; } private: std::string value; }; extern "C" void getRef(std::string& v) { auto h = Holder::create(); v = h->getRef(); }
我通过P/Invoke调用getRef方法,未使用特殊封送处理。请问这种写法能保证正常运行吗?h会在getRef方法结束时超出作用域,但它的成员value的引用被保留并传递出去,在C#端访问该引用时是否仍然有效?
问题分析与解答
这种写法完全不能保证正常运行,C#端访问该引用时必然会触发无效内存访问,你遇到的偶发崩溃就是这个原因导致的。
核心原因拆解:
- 在C++的
getRef函数中,h是局部的std::shared_ptr<Holder>,函数执行完毕时h会离开作用域,对应的Holder对象引用计数归零,随即被销毁。 Holder对象销毁时,其成员value(std::string类型)会被析构,底层内存会被释放或标记为可复用状态。- 你通过
v = h->getRef()将value的引用赋值给参数v,但.NET的P/Invoke默认没有针对std::string的内置封送逻辑,直接传递引用本质上是传递内存地址。当Holder对象销毁后,该地址指向的内存已经无效,C#端访问时会触发未定义行为——可能直接崩溃、读取到垃圾数据,甚至在内存未被覆盖的偶然情况下看似正常,这就是“偶发崩溃”的由来。
总结:h销毁后,value的内存已被回收,C#端拿到的内存地址指向无效内存,访问它必然出问题。
内容的提问来源于stack exchange,提问作者mspiller
相关产品推荐
相关产品推荐

