分配内存并调用C++回调的Rust函数崩溃问题求助
问题分析与修复方案
嘿,我一眼就发现你代码崩溃的根源了——悬垂指针!咱们来拆解下问题,再给你解决办法:
问题出在哪?
看你set_callback里这段代码:
#[no_mangle] pub extern "C" fn set_callback(callback: extern "C" fn(i: i32)) -> *mut Tmp { let mut tmp = Box::new(Tmp { callback }); println!("tmp as ptr: {:p}", tmp); // >> 这里就是坑 << &mut *tmp }
你用Box::new在堆上分配了Tmp的内存,但函数结束时,tmp这个Box变量会离开作用域,Rust的自动内存回收会直接释放这块堆内存!而你返回的&mut *tmp只是从Box里抠出来的裸指针,此时它指向的内存已经被回收了——后续调用use_callback去访问这个指针,本质就是访问已经失效的内存,不崩溃才怪😅
怎么修复?
我们需要把堆内存的所有权从Rust转移到C++侧,让Rust不要自动释放它,直到我们手动告诉它可以回收。用Box::into_raw就能做到这一点:
修改后的完整Rust代码:
#[repr(C)] pub struct Tmp { pub callback: extern "C" fn(i: i32), } #[no_mangle] pub extern "C" fn set_callback(callback: extern "C" fn(i: i32)) -> *mut Tmp { let tmp = Box::new(Tmp { callback }); println!("tmp as ptr: {:p}", tmp); // 把Box转成裸指针,所有权转移给C++,Rust不会再自动释放 Box::into_raw(tmp) } #[no_mangle] pub extern "C" fn use_callback(tmp_ptr: *mut Tmp) { unsafe { // 先检查指针非空,避免空指针崩溃(可选但推荐) if !tmp_ptr.is_null() { ((*tmp_ptr).callback)(1); ((*tmp_ptr).callback)(3); } } } // 新增:给C++调用的内存回收函数,必须调用它避免内存泄漏 #[no_mangle] pub extern "C" fn free_tmp(tmp_ptr: *mut Tmp) { unsafe { if !tmp_ptr.is_null() { // 把裸指针转回Box,Rust会自动帮我们释放内存 Box::from_raw(tmp_ptr); } } }
对应的C++代码也要配合调用free_tmp来释放内存:
#include <cstdint> #include <cstdio> struct Tmp { void (*callback)(int32_t); }; // 声明Rust导出的函数 extern "C" { Tmp* set_callback(void (*callback)(int32_t)); void use_callback(Tmp* tmp_ptr); void free_tmp(Tmp* tmp_ptr); } // 你的回调函数示例 void my_callback(int32_t val) { printf("Callback got value: %d\n", val); } int main() { Tmp* tmp = set_callback(my_callback); use_callback(tmp); // 用完一定要记得释放内存! free_tmp(tmp); return 0; }
关键点说明
Box::into_raw:把Box<T>转换成裸指针*mut T,这个操作会让Rust放弃对这块堆内存的管理权,不会自动释放,直到我们用Box::from_raw把它转回来。Box::from_raw:把裸指针转回Box<T>,当这个Box离开作用域时,Rust就会自动释放对应的内存——注意必须保证指针是Box::into_raw生成的,不然会触发未定义行为。- 空指针检查:在unsafe代码里加个
!tmp_ptr.is_null()的判断,能避免不小心传入空指针导致的崩溃,算是个好习惯。
内容的提问来源于stack exchange,提问作者user7810882
相关产品推荐
相关产品推荐

