如何从Str获取*mut c_char?

6
要使用C库,我需要给一个函数传递一个*mut c_char参数。但是我不知道如何从str中获取它。我将我的str转换为CString,这很好,但在nightly版本中已经没有从CString获取*mut c_char的方法了。我发现在0.12.0版本中有一个方法,但现在,如何获取那个*mut c_char呢?

你有哪种类型的字符串,是 &str 还是 String - Shepmaster
请在C函数中包含签名,因为它将包含有关如何调用该方法的详细信息。具体而言,我想知道函数如何知道字符串中有多少空间,以便不会超出分配的内存。 - Shepmaster
理想情况下,CString 应该有 DerefMut 实现,并且 Output = [c_char],这样就可以通过 c_str.as_mut_ptr() 获取 *mut c_char。但我认为没有这样的实现的原因是它可能会破坏 CString 的保证,即它不包含零:在这种情况下,c_str[2] = 0 是可能的。所以,我认为你的基于 Vec 的解决方案很好;只要不忘记在末尾推入一个零字节,如果你的 C API 需要一个零结尾的字符串,那就没问题了。 - Vladimir Matveev
6个回答

6
let bytes = String::from_str("Test").into_bytes() + b"\0";
let cchars = bytes.map_in_place(|b| b as c_char);
let name: *mut c_char = cchars.as_mut_ptr();

基本思路与您相同,但没有必要显式地切片 Vec;同时,在缓冲区末尾添加了一个零字节。另请参阅我对问题的评论。


谢谢,其实总是少调用一个函数更好^^ - Peekmo
2
很遗憾,这个答案在2021年已经不适用了 :( - Kevin Burke

4
五年过去了,这些解决方案都不起作用(至少对我来说是这样)。
这是此答案的稍微修改版:
let string: &str = "Hello, world!";
let bytes: Vec<u8> = String::from(string).into_bytes();
let mut c_chars: Vec<i8> = bytes.iter().map(| c | *c as i8).collect::<Vec<i8>>();

c_chars.push(0); // null terminator

let ptr: *mut c_char = c_chars.as_mut_ptr();

2
这前两行代码可以简化为 let bytes = b"Hello, world!".to_vec(); - Herohtar

2

现在(2022年),into_raw() 对我来说很好用,例如:

use std::ffi::CString;
use std::os::raw::c_char;
fn main() {    
  let c_string = CString::new("Hello!").expect("CString::new failed");
  let raw: *mut c_char = c_string.into_raw();
}

请查看https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw以获取更多相关信息。

1
你需要小心使用into_raw,因为它会导致内存泄漏,除非你将其转换回一个CString。 - jacob_pro

1
看一下这篇文档fn as_mut_ptr()已经移到API的slice部分。因此,您需要一个可变的&mut [c_char]类型的切片。据我所知,您无法从CString中获取它们,因为那些是只读的。

相反,您可以使用mut Vec<c_char>

let mut x : Vec<c_char> = ...;
let slice = x.as_mut_slice();
let ptr = slice.as_mut_ptr();

0
感谢rodrigo的帖子,我找到了一种解决我的问题的方法(非常不好看,但它有效)。以下是代码:
let mut string = std::string::String::from_str("Test");
let bytes = string.into_bytes();
let mut cchar : Vec<c_char> = bytes.map_in_place(|w| w as c_char);
let slice = cchar.as_mut_slice();
let name: *mut c_char = slice.as_mut_ptr();

在我看来有点复杂


不错,但我认为你可以省略“map_in_place”。也许编译器能够优化掉那段代码,因为“u8”和“c_char”(又名“i8”)保证是位相同的。 - rodrigo
不加上 map_in_place 是无法编译的 ;) - Peekmo
我的意思是,你可以声明 name : *mut u8 并相应地更改 API 导入代码,一切都应该正常工作。或者也许你可以使用 transmute(),但我不知道确切的规则。 - rodrigo

0

如果你想要

let mut hello =   b"Hello World\0".to_vec().as_mut_ptr() as *mut i8;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接