我正在尝试在Rust中包装一个C函数。 C函数struct elem* get_list()
返回以下结构:
struct elem {
char data[5],
struct elem* next
};
在Rust中,我已按以下方式声明了该函数。 C函数的声明返回*const c_void
,如旧版本的Rust文档中所述,在撰写本文时我无法找到。我尝试返回*const elem
并使用指针,实现相同的结果:
extern "C" {
pub fn get_list() -> *const c_void;
}
struct表示链表,next
是指向列表下一个元素的指针。在Rust内部,我以下列方式声明了结构:
#[repr(C)]
pub struct elem {
pub data: [u8; 5],
pub next: *const c_void,
}
该函数返回一个*const c_void
指针,指向链表的第一个元素(类型为elem
)。我正在尝试使用以下代码读取链表的元素:
let head = get_list();
while !head.is_null() {
let el: &elem = mem::transmute(head);
let str = el.data;
let str = CStr::from_bytes_with_nul(&str).unwrap();
//do something
head = el.next();
}
这会读取垃圾数据 - 指针未正确对齐,字符串都是错误且非空终止,下一个指针导致随机数据(当从C直接调用函数时,列表具有不同的大小)
我尝试使用该函数返回指向elem
的指针并仅使用指针,我尝试从str
的地址转换el
- 它总是读取相同的垃圾数据。如何正确对齐?
我知道如何使用指针而不是数组来执行此操作,这就是在Rust文档中演示的方式,但我无法更改C代码。
答案 0 :(得分:1)
在我为这个案例编写了一个示例库之后,我发现它不是一个外部问题,而是一个CStr
问题。正如在示例中修复的那样,我将缓冲区切片到第一个NUL终止符的位置,我提供了我为正确的externing编写的示例。
<强> list.c 强>
#include <stdlib.h>
#include <string.h>
struct elem {
char data[5];
struct elem* next;
};
struct elem* get_list() {
struct elem* head = malloc(sizeof(struct elem));
strcpy(head->data, "1");
struct elem* el = malloc(sizeof(struct elem));
head->next = el;
strcpy(el->data, "2");
el->next = malloc(sizeof(struct elem));
el = el->next;
strcpy(el->data, "3");
el->next = NULL;
return head;
}
<强> main.rs 强>
use std::ffi::CStr;
#[repr(C)]
pub struct elem {
pub data: [u8; 5],
pub next: *const elem
}
#[link(name = "list", kind = "static")]
extern {
pub fn get_list() -> *const elem;
}
fn main() {
unsafe {
let mut list = get_list();
// Note, that, if we call from_bytes_with_nul it will throw
// an NulInternal error, therefore,
// we have to slice the buffer to the first NUL-terminator
while !list.is_null() {
let mut null_pos = (*list).data.len() - 1;
{
for i in 0..(*list).data.len() {
if (*list).data[i] == 0 {
null_pos = i + 1;
break
}
}
}
let str = CStr::from_bytes_with_nul(
(*list).data[..null_pos]
).unwrap();
println!("{:?}", str);
list = (*list).next;
}
}
}
输出
"1"
"2"
"3"
实施的关键方面:
定义相同的结构,用#[repr(C)]
注释,这样就可以了
以与C one相同的方式对齐。
定义extern函数以返回指向结构的const指针。
使用指针代替std::mem::transmute
小心使用空指针和终结符