将 Vec<String> 从 Rust 传递到 C 中的 char**
Passing Vec<String> from Rust to char** in C
我一直在尝试用 Rust 编写一个直接链接到 libc
库的 shell。我已经使用 Vec<String>
来保存要传递给 execvp()
的参数,但似乎我转换为 char **
没有成功。执行后,所有参数都变成了空字符串。
这是涉及的代码。
fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> {
unsafe {
let c_path = CString::new(path.as_str()).unwrap();
let mut c_argv_vec = Vec::new();
for arg in &argv {
let c_arg = CString::new(arg.as_str()).unwrap().as_ptr();
c_argv_vec.push(c_arg);
}
c_argv_vec.push(std::ptr::null());
match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) {
num => Err(num),
}
}
}
execvp
是定义为fn execvp(file: *const i8, argv: *const*const i8) -> i32;
.
的C库函数
我不确定我做错了什么。是因为参数的内存在调用execvp()
之前被释放了吗?
我建议你再读一遍the documentation for CString::as_ptr
:
WARNING
It is your responsibility to make sure that the underlying memory is not
freed too early. For example, the following code will cause undefined
behavior when ptr
is used inside the unsafe
block:
# #![allow(unused_must_use)]
use std::ffi::{CString};
let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
unsafe {
// `ptr` is dangling
*ptr;
}
This happens because the pointer returned by as_ptr
does not carry any
lifetime information and the CString
is deallocated immediately after
the CString::new("Hello").expect("CString::new failed").as_ptr()
expression is evaluated.
To fix the problem, bind the CString
to a local variable:
# #![allow(unused_must_use)]
use std::ffi::{CString};
let hello = CString::new("Hello").expect("CString::new failed");
let ptr = hello.as_ptr();
unsafe {
// `ptr` is valid because `hello` is in scope
*ptr;
}
This way, the lifetime of the CString
in hello
encompasses
the lifetime of ptr
and the unsafe
block.
您所做的正是文档中禁止做的事情。
您正在创建 CString
个实例并立即获取指针。因此,正如您猜到的那样,这个字符串的所有权被过早地删除了。这类似于返回对本地实例的引用,只是由于指针不保留生命周期信息,因此这种情况不会触发编译错误。
您的问题的解决方案是在函数范围内保留拥有的 C 样式字符串,并分别生成指向相同内容的指针的指针。
let cstr_argv: Vec<_> = argv.iter()
.map(|arg| CString::new(arg.as_str()).unwrap())
.collect();
let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter()
.map(|arg| arg.as_ptr())
.collect();
p_argv.push(std::ptr::null());
let p: *const *const c_char = p_argv.as_ptr();
另请参阅:
我一直在尝试用 Rust 编写一个直接链接到 libc
库的 shell。我已经使用 Vec<String>
来保存要传递给 execvp()
的参数,但似乎我转换为 char **
没有成功。执行后,所有参数都变成了空字符串。
这是涉及的代码。
fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> {
unsafe {
let c_path = CString::new(path.as_str()).unwrap();
let mut c_argv_vec = Vec::new();
for arg in &argv {
let c_arg = CString::new(arg.as_str()).unwrap().as_ptr();
c_argv_vec.push(c_arg);
}
c_argv_vec.push(std::ptr::null());
match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) {
num => Err(num),
}
}
}
execvp
是定义为fn execvp(file: *const i8, argv: *const*const i8) -> i32;
.
我不确定我做错了什么。是因为参数的内存在调用execvp()
之前被释放了吗?
我建议你再读一遍the documentation for CString::as_ptr
:
WARNING
It is your responsibility to make sure that the underlying memory is not freed too early. For example, the following code will cause undefined behavior when
ptr
is used inside theunsafe
block:# #![allow(unused_must_use)] use std::ffi::{CString}; let ptr = CString::new("Hello").expect("CString::new failed").as_ptr(); unsafe { // `ptr` is dangling *ptr; }
This happens because the pointer returned by
as_ptr
does not carry any lifetime information and theCString
is deallocated immediately after theCString::new("Hello").expect("CString::new failed").as_ptr()
expression is evaluated. To fix the problem, bind theCString
to a local variable:# #![allow(unused_must_use)] use std::ffi::{CString}; let hello = CString::new("Hello").expect("CString::new failed"); let ptr = hello.as_ptr(); unsafe { // `ptr` is valid because `hello` is in scope *ptr; }
This way, the lifetime of the
CString
inhello
encompasses the lifetime ofptr
and theunsafe
block.
您所做的正是文档中禁止做的事情。
您正在创建 CString
个实例并立即获取指针。因此,正如您猜到的那样,这个字符串的所有权被过早地删除了。这类似于返回对本地实例的引用,只是由于指针不保留生命周期信息,因此这种情况不会触发编译错误。
您的问题的解决方案是在函数范围内保留拥有的 C 样式字符串,并分别生成指向相同内容的指针的指针。
let cstr_argv: Vec<_> = argv.iter()
.map(|arg| CString::new(arg.as_str()).unwrap())
.collect();
let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter()
.map(|arg| arg.as_ptr())
.collect();
p_argv.push(std::ptr::null());
let p: *const *const c_char = p_argv.as_ptr();
另请参阅: