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

Rust转C字符串首字节被置零问题及代码有效性咨询

Rust FFI字符串转换首字节置零问题分析与修复

问题重现

原代码尝试将Rust字符串编码为Windows-1251的C风格零终止字符串供FFI调用,但运行后出现首字节被置零的异常:

原代码

use encoding::all::WINDOWS_1251;
use encoding::{EncoderTrap, Encoding};
use std::ffi::{c_char, CString};

fn str_to_c_str(input: &str) -> *const c_char {
    let enc_input: Vec<u8> = WINDOWS_1251.encode(input, EncoderTrap::Strict).expect("TODO: panic message");
    println!("enc_input {:?}", enc_input);
    let cstring: CString = CString::new(enc_input).expect("TODO: panic message");
    let s1 = unsafe { std::slice::from_raw_parts(cstring.as_ptr(), 4) };
    println!("str_to_c_str {:?}", s1);
    cstring.as_ptr()
}

fn main() {
    unsafe {
        let data = str_to_c_str("ABC");
        let s2 = unsafe { std::slice::from_raw_parts(data, 4) };
        println!("main {:?}", s2);
    }
}

实际输出

enc_input [65, 66, 67]
str_to_c_str [65, 66, 67, 0]
main [0, 66, 67, 0]

依赖配置

[dependencies]
libc = "0.2.0"
encoding = "0.2"
byteorder = "1.4.3"

问题原因

核心问题是悬空指针:

  • str_to_c_str函数中创建的CString是局部变量,函数执行完毕返回时,CString的析构函数会立即释放它管理的内存
  • 返回的*const c_char指针此时指向已被回收的内存,后续访问时内存内容已被系统或其他操作覆盖,因此出现首字节置零的异常

修复方案

解决的核心是保证CString的生命周期覆盖指针的使用周期,避免悬空指针:

  • 直接返回CString对象而非裸指针,由调用方持有并管理其生命周期(即你改写后的代码思路)
  • 若必须返回裸指针,需手动延长CString的生命周期(比如用Box::leak将内存泄漏,后续需手动调用C的free释放,但这种方式易引发内存泄漏,不推荐)

改写后代码的有效性

你提供的改写代码是完全有效的:

use encoding::all::WINDOWS_1251;
use encoding::{EncoderTrap, Encoding};
use std::ffi::{c_char, CString};

unsafe fn ffi_call(arg: *const c_char) -> () {
    let s1 = unsafe { std::slice::from_raw_parts(arg, 4) };
    println!("str_to_c_str {:?}", s1);
}

fn str_to_c_str(input: &str) -> CString {
    let enc_input: Vec<u8> = WINDOWS_1251.encode(input, EncoderTrap::Strict).expect("TODO: panic message");
    println!("enc_input {:?}", enc_input);
    let cstring: CString = CString::new(enc_input).expect("TODO: panic message");
    cstring
}

fn main() {
    let data = str_to_c_str("ABC");
    // 1
    unsafe {
        ffi_call(data.as_ptr());
    }
    // 2
}
  • main函数中的data变量持有CString对象,其生命周期从创建到main函数结束
  • 调用ffi_call时,data仍处于存活状态,as_ptr()返回的指针指向有效内存,FFI调用能正确读取字符串内容
  • 运行后会输出预期的[65, 66, 67, 0]

关于编译器优化的疑问

编译器不会提前释放CString的内存:
Rust的所有权系统严格保证,只要CString对象(这里的data)还在当前作用域内,编译器就不会对其进行提前销毁的优化。data在main函数中直到注释2之后才会被销毁,完全覆盖了FFI调用的周期,因此内存是安全有效的。

内容的提问来源于stack exchange,提问作者osseum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:54:56