Rust与C互操作中char指针异常问题及原理咨询
Rust-C互操作:段错误与指针类型问题解析
问题根源定位
初始Rust代码存在致命错误:
let content_bytes = content_buf.as_bytes()[0] as *const u8;
这里content_buf.as_bytes()[0]取的是字符串第一个字节的数值(比如'A'对应ASCII值65),随后被强制转换成*const u8类型指针——相当于让C函数去访问内存地址0x41(65的十六进制),这属于非法内存访问,直接触发段错误。
正确的写法应该是用as_ptr()获取字节数组的合法指针:
let content_bytes = content_buf.as_bytes().as_ptr();
异常现象分析(修改C代码后的奇怪输出)
当你把C代码中的*(file_contents + i)改为(file_contents + i)后,本质是把指针地址当成字符输出:
file_contents此时是错误的非法指针(比如0x41),file_contents + i就是0x41 + i的地址值printf("%c", ...)会把这个地址的低8位当作ASCII字符,因此输出'A'(0x41)、'B'(0x42)...以此类推,长度和文件内容一致- 首字符正确纯属巧合:非法指针的起始地址刚好对应原字符串首字符的ASCII值
疑问解答
1. C视角下*const u8与*const [u8]的底层区别
*const u8是Rust的单字节裸指针,对应C中的const uint8_t*(或Ubuntu上的const char*,因为系统中char为8位),本质就是一个单一内存地址,C中可通过指针算术访问连续内存。*const [u8]是Rust的切片胖指针,底层是两个64位值(64位系统下):- 第一个值:切片起始位置的指针(和
*const u8的地址一致) - 第二个值:切片的长度
当你把它传给C函数时,C函数只会读取第一个参数的地址值,你手动传入的file_size刚好覆盖了胖指针的长度字段,巧合匹配需求才让程序正常运行,但这属于未定义行为——切片指针不符合C的ABI规范,因此编译器会警告[u8]非FFI安全。
- 第一个值:切片起始位置的指针(和
2. 为何*const u8的指针算术会触发段错误
不是*const u8的指针算术本身有问题,而是你初始化content_bytes的方式错误:你把字节值转成了指针,而非获取数组的合法指针。C函数执行file_contents + i时,是在非法内存地址上做偏移,访问的都是不允许的内存区域,最终触发段错误。
正确代码实现
C代码(保持原逻辑即可)
// print_file.c #include <stdio.h> void print_file(const char *file_contents, size_t file_size) { for (size_t i = 0; i < file_size; i++) printf("%c", *(file_contents + i)); }
Rust代码(修正指针获取方式)
// main.rs use std::{fs::OpenOptions, io::Read}; use libc::size_t; #[link(name = "printer")] extern "C" { fn print_file( file_contents: *const u8, file_size: size_t ); } fn main() { let mut content_buf: String = String::new(); let mut file = OpenOptions::new().read(true).open("content.txt").unwrap(); file.read_to_string(&mut content_buf).unwrap(); let file_size: size_t = content_buf.len(); // 正确获取字节数组的指针 let content_bytes = content_buf.as_bytes().as_ptr(); unsafe { print_file(content_bytes, file_size); } }
内容的提问来源于stack exchange,提问作者Radon
相关产品推荐
相关产品推荐

