传递 CString 后跟 int 时 FFI 中的错误

Bug in FFI when passing CString followed by an int

我的 Rust 测试代码

extern "C" {
fn test_int_only(n : libc::c_int);
fn test_int_and_str(s : CString , n : libc::c_int);
}

pub fn test1() { 
unsafe {
    test_int_only(0);
    test_int_only(1);
    test_int_only(2);
    test_int_only(4);
    test_int_only(-12);
    }
}


pub fn test2() { 
unsafe {
    test_int_and_str(CString::new("Foo").unwrap(),0);
    test_int_and_str(CString::new("Bar").unwrap(),1);
    test_int_and_str(CString::new("Baz").unwrap(),2);
    test_int_and_str(CString::new("Fub").unwrap(),4);
    test_int_and_str(CString::new("Bub").unwrap(),-12);
    }
}

我的C代码

void test_int_only(int abc){
    printf("%d\n", abc);
}

void test_int_and_str(const char* name,int abc) {
    printf("%s %d\n", name, abc);
}

测试 test_int_only()

1
2
4
-12

测试 test_int_and_str()

Foo 4
Bar 4
Baz 4
Fub 4
Bub 4

似乎第二个 arg 被解释(在 rust 或 c 中)为 sizeof 字符串,而不是从 Rust 代码传递的值。我猜这与调用约定或空终止无法正常工作有关。这是一个 C dll,带有 _cdecl (windows 32bit dll) 调用约定。有趣的是,传递一个(不透明的)指针和一个 int(在另一个测试中)工作正常,所以我不认为这是一个调用约定问题。

It seems that the 2nd arg is being interpreted (either in rust or c) as sizeof string, rather than the value passed from the Rust code.

正确。您在这里遇到未定义的行为。

您的 C 函数与您在 Rust 代码中声明的 extern 函数具有不同的签名。首先,将不是 #[repr(C)] 的类型传递给外部函数是未定义的行为,并且曾经有一个用于该 afaik 的 lint。其次,CString 不是 char*,它是具有内部数据的结构。如果你想传递一个const char*,你必须传递一个*const u8。您可以通过 into_ptr 函数从 CString 获取这样的指针。

注意 into_ptr 泄漏了内存,您需要再次使用 from_ptr 来获得一个可以被释放的 CString 对象。 如果你只想借出 CString 对象,你可以按照以下几行做一些事情:

// ensure c_str lives longer than the function call
let c_str = CString::new("Foo").unwrap();
unsafe { test_int_and_str(c_str.as_ptr(), 0); }
// let the destructor of c_str free the memory