gpt4 book ai didi

c - 将数组分配给* mut c_void

转载 作者:太空狗 更新时间:2023-10-29 15:26:12 24 4
gpt4 key购买 nike

我正在为库编写绑定(bind),其中在Rust中有一个参数类型为void* aka *mut c_void的函数。我必须为此参数分配一个数组,如何在Rust中做到这一点?

我试过强制转换transmute,它不起作用(transmutec_void[u8]的大小不同)。如果有关系,我将从 vector 中获取切片。

更新:也许以某种方式使用vec.as_mut_ptr()可能是正确的?

PLAYPEN :http://is.gd/KjgduZ

最佳答案

您描述的API看起来非常可疑。请记住,实际上C中没有“数组”-数组只是指针的另一个名称,该指针指向连续放置在内存中的相同类型的多个值的开头。因此,不可能仅在C语言中“分配”一个数组。有两个概念可以理解为“分配”到数组:首先,在某个地方分配一个指向数组开头的指针:

const char *s1 = "hello";
const char *s2 = "world";

const char *s = s1; // make `s` contain a pointer to "hello"
s = s2; // make `s` contain a pointer to "world"

其次,它是将某些数据从一个指针复制到另一个指针,这通常是使用 memcpy()或类似的方法完成的:

const char *s1 = "hello";

char s2[5];
memcpy(s2, s1, 5); // copy 5 bytes from the memory pointed at by `s1` to the memory pointed at by `s2`

当我说您的API可疑时,您现在可能已经明白我的意思了。您的回调函数被指定为 void *,但是,没有指示应使用哪种“数组复制”方法。

如果是第一个,即将指针复制到数组的开头,那么 void *类型是非常无用的。它没有说该指针应该如何表示。您似乎正试图做到这一点;但是,它不会像您想的那样起作用。这是您的代码的编译变体(请注意,这是错误的,很可能会使您的程序崩溃;请参见下文):

#![feature(libc)]
extern crate libc;

use libc::c_void;

pub extern fn demo(data: *mut *mut c_void) {
let mut vec = vec!(1, 2, 3);
unsafe {
*data = vec.as_mut_ptr() as *mut c_void;
}
}

(请注意,由于autoderef的原因,您可以直接在包含 vector 的 as_mut_ptr()变量上调用 mut)

现在,参数类型不仅是 *mut c_void,而且是 *mut *mut c_void,即它是 *mut c_void的指针。这样,调用此函数的程序可以将指向 void *类型的局部变量的指针传递给该函数,并获得指向实际数组的指针,例如

void *data;
some_struct.callback_fn(&data); // pointer to `demo` is stored in `some_struct`
// data is now whatever your `demo` function has assigned

请注意,您无法明智地使 demo仅接受 *mut c_void,因为您唯一可以做的就是重新分配参数本身,但是重新分配参数只会重新分配此参数值,即此参数表示的局部变量。在函数外部无法观察到这一点。换句话说,以下代码(也是您提供的代码的变体):

pub extern fn demo(mut data: *mut c_void) {
let mut vec = vec!(1, 2, 3);
data = vec.as_mut_ptr() as *mut c_void;
}

什么都不做,Rust很高兴指出这一点:

<anon>:6:20: 6:28 warning: variable `data` is assigned to, but never used, #[warn(unused_variables)] on by default
<anon>:6 pub extern fn demo(mut data: *mut c_void) {
^~~~~~~~
<anon>:8:5: 8:9 warning: value assigned to `data` is never read, #[warn(unused_assignments)] on by default
<anon>:8 data = vec.as_mut_ptr() as *mut c_void;
^~~~

我说 *mut *mut c_void的代码错误的原因是,它实际上违反了内存安全性。如果创建 Vec实例并将其存储到本地变量,则当此变量超出范围时, vector 本身将被销毁,并且其包装的内存将被释放。因此,使用 as_ptr()as_mut_ptr()从其获得的每个指针都将变为无效。

有几种方法可以解决此问题,最简单的方法是对 vector ​​进行 forget()编码:

use std::mem;

let mut vec = vec![1, 2, 3];
*data = vec.as_mut_ptr() as *mut c_void;
mem::forget(vec);

这样, vector 被“遗忘了”-它的析构函数将不会被调用。但是,这种方式会在程序中引入内存泄漏。每次调用 demo()时,会分配更多的内存,但不会释放更多的内存,因此最终,您的程序将使用所有可用的内存,之后可能会崩溃。但是,在某些情况下这样做是明智的,尤其是在低级代码中。例如,您的API可能指定仅将调用此函数一次。

这种方法的另一个问题是上述方法的逻辑后果。您的API可以指定谁应该释放提供给它的指针处的内存。例如,它可能需要传递一个分配了 malloc()的内存,这样它才可以单独使用 free()释放它。或者它可以指定您应该定义另一个函数,当所有分配的内存都应释放时将调用该函数。两种方法都很难在Rust中实现;除非确实是您的情况,否则我不会详细介绍如何执行此操作。无论如何,您的API必须明确指定内存的拥有者,并且应将其考虑在内,因为Rust更明确地说明了所有权。

另一种可能性是您的API要求您将一些数据复制到 void *指针指定的内存中。换句话说,其实现包含这样的代码:

char buffer[256];
some_struct.callback_fn(buffer);

并期望在调用 callback_fn之后, buffer中会填充数据。

在这种情况下,API自然必须指定程序可以使用的缓冲区中的最大字节数,并且 demo函数可能如下所示:

use std::ptr;
use libc::c_void;

pub extern fn demo(data: *mut c_void) {
let vec: Vec<u8> = vec!(1, 2, 3);
unsafe {
ptr::copy_nonoverlapping(vec.as_ptr(), data as *mut u8, vec.len());
}
}

(或者,您可以使用 data &mut [u8]转换为 std::slice::from_raw_parts_mut()并使用 clone_from_slice() 方法或 bytes::copy_memory() 函数,但它们都是不稳定的,因此不能在稳定的Rust上使用)

在这种情况下,您应格外小心,不要使调用程序提供给您的缓冲区溢出。其最大大小应在API中指定。

另一个问题是,仅对字节数组(在C端使用 char *,在Rust端使用 &[u8]/&mut [u8])复制数组很简单。当您开始使用更大的类型(例如 i32)时,您将有可能出现可移植性问题。例如,在 Cint没有定义的大小,因此您不能盲目地将 &[i32]转换为原始大小四倍的 &[u8]并将其字节复制到 *mut u8。这些问题应非常小心地处理。

关于c - 将数组分配给* mut c_void,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31759582/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com