You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

分配内存并调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:28:57