Skip to content

Ch 17: CStr与CString

Rust与其他语言(尤其是C)的交互需要使用FFI(Foreign Function Interface)。C语言使用null终止的字符串,Rust的String和&str不是null终止的。CString和CStr就是为此设计的:

  • CString - 拥有所有权的null终止字符串,用于传递给C函数
  • CStr - null终止字符串的引用,用于从C接收字符串
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
fn main() {
// 从&str创建CString(自动添加null终止符)
let c_string = CString::new("hello").unwrap();
println!("CString: {:?}", c_string);
// 获取原始指针
let ptr: *const c_char = c_string.as_ptr();
println!("指针: {:?}", ptr);
// 转为字节形式
let bytes: &[u8] = c_string.as_bytes();
println!("字节: {:?}", bytes);
// 注意:字节中不包含结尾的null
let bytes_with_nul: &[u8] = c_string.as_bytes_with_nul();
println!("带null字节: {:?}", bytes_with_nul);
}
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
fn main() {
// 模拟C函数:int strlen(const char* s)
extern "C" {
fn strlen(s: *const c_char) -> usize;
}
let s = CString::new("hello").unwrap();
// 安全调用C函数
unsafe {
let len = strlen(s.as_ptr());
println!("strlen: {}", len); // 输出: 5
}
}
use std::ffi::CString;
use std::process::Command;
fn main() {
// 使用CString调用系统命令
let program = CString::new("ls").unwrap();
let arg = CString::new("-la").unwrap();
// 很多系统API接受*Cmut类型的参数用于输出
// 这是与C交互的常见模式
}
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::str;
fn main() {
// 模拟从C函数获取字符串
extern "C" {
fn get_greeting() -> *const c_char;
}
unsafe {
let ptr = get_greeting();
if ptr.is_null() {
println!("返回了null指针");
return;
}
// 从指针创建CStr
let c_str = CStr::from_ptr(ptr);
println!("CStr: {:?}", c_str);
// 转换为&str
let str_slice: &str = c_str.to_str().unwrap();
println!("&str: {}", str_slice);
// 或转换为String
let string: String = c_str.to_string_lossy().into_owned();
println!("String: {}", string);
}
}
use std::ffi::CStr;
fn main() {
// 模拟无效UTF-8
let invalid_bytes: &[u8] = b"hello\xff\xfeworld";
let c_str = unsafe {
// 实际中CStr应从C指针创建,这里演示to_string_lossy
CStr::from_bytes_with_nul(invalid_bytes).unwrap()
};
// to_string_lossy用U+FFFD替换无效字符
let lossy_string = c_str.to_string_lossy();
println!("lossy: {}", lossy_string);
}
方法说明
from_ptr(ptr)从C指针创建CStr
from_bytes_with_nul(bytes)从字节数组创建(包含null)
to_str()转换为&str(可能失败)
to_string_lossy()转换为String,替换无效字符
to_bytes()转换为字节(不含null)
to_bytes_with_nul()转换为字节(含null)
len()字节长度(不含null)
is_empty()是否为空
use std::ffi::CString;
fn main() {
// CString会在drop时释放内存
// 但如果直接获取原始指针并忘记,CString drop后会造成问题
let c_string = CString::new("hello").unwrap();
let ptr = c_string.as_ptr();
// 错误:如果在这里让c_string drop,指针将无效
// drop(c_string);
// unsafe { println!("{}", *ptr); } // 未定义行为!
// 正确做法:保持CString活着,或复制数据
println!("指针指向: {:?}", unsafe {
std::ffi::CStr::from_ptr(ptr).to_string_lossy()
});
}
use std::ffi::CString;
fn main() {
// 空字符串
let empty = CString::new("").unwrap();
println!("空CString字节: {:?}", empty.as_bytes_with_nul());
// 输出: [0]
// 包含null字符(内部包含)
let with_null = CString::new("a\x00b").unwrap();
println!("带null字节: {:?}", with_null.as_bytes_with_nul());
// 输出: [97, 0, 98, 0]
}
use std::ffi::CStr;
use std::os::raw::c_char;
fn main() {
// Box<CStr> 将CStr堆分配
let boxed: Box<CStr> = unsafe {
let c_str = CStr::from_bytes_with_nul(b"hello\0").unwrap();
Box::new(c_str)
};
println!("boxed: {:?}", boxed);
// CStr不支持直接Box::new,需要这样
// 更常见的是从C函数返回的指针Box
}
use std::ffi::CString;
use std::os::raw::c_char;
#[repr(C)]
pub struct Dirent {
pub d_name: *const c_char,
}
fn main() {
// 实际使用中,如POSIX的readdir返回的Dirent结构
// d_name字段是指向null终止字符串的指针
println!("使用CStr处理来自C库的字符串");
}
use std::ffi::{CString, CStr};
use std::ptr;
fn main() {
// C函数可能返回null表示错误
// 正确处理null检查
unsafe fn get_c_string() -> *const c_char {
ptr::null() // 模拟返回null
}
let result = unsafe {
let ptr = get_c_string();
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
}
};
match result {
Some(s) => println!("Got: {}", s),
None => println!("Got null"),
}
}

今天我们学习了CStr和CString:

  1. CString - 拥有所有权的null终止字符串,用于传递给C函数
  2. CStr - null终止字符串的引用,用于从C接收字符串
  3. 创建方式 - CString::new()从&str创建
  4. 转换方法 - to_str()、to_string_lossy()、as_ptr()
  5. 注意事项 - 内存所有权、null指针检查、无效UTF-8处理

FFI互操作需要小心处理内存所有权和生命周期,正确使用CString和CStr可以安全地与C代码交互。