You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust与C的FFI交互:如何通过结构体[u8;1]成员传递指针

Rust与C互操作:对应C柔性数组结构体的定义与使用

C语言中存在如下结构体:

struct Foo{
    unsigned int    body_len;
    unsigned char   body[1];    
}

问题:如何在Rust中定义对应的Foo结构体,以及如何创建Foo实例并传递给接收Foo*参数的外部C函数,使得C端能够访问body数据(该body成员会在C函数中转换为unsigned char*)。


一、Rust中定义匹配的结构体

C代码里的body[1]是柔性数组(变长数组),本质是作为占位符,实际数据会紧跟在结构体内存之后。Rust中要保证内存布局和C完全一致,必须使用#[repr(C)]属性,同时用对应类型匹配:

#[repr(C)]
#[derive(Debug)]
pub struct Foo {
    pub body_len: u32,  // 对应C的unsigned int
    pub body: [u8; 1],  // 对应C的unsigned char[1]
}
  • #[repr(C)]强制Rust采用C语言的内存对齐和布局规则,确保结构体在内存中的排布和C端完全一致。
  • u32和u8分别对应C的unsigned int和unsigned char,保证类型宽度匹配。

二、创建实例并传递给C函数

由于柔性数组的特性,我们需要手动分配一块连续内存,同时容纳Foo结构体本身和后续的body数据,步骤如下:

1. 依赖准备

首先在Cargo.toml中添加libc crate,用于调用C标准库的内存分配函数:

[dependencies]
libc = "0.2"

2. 实现实例创建与C函数调用

use libc::{malloc, free, c_void};
use std::ptr;
use std::mem;

// 声明外部C函数(替换为你的实际函数名和库名)
#[link(name = "your_c_library")]
extern "C" {
    fn process_foo(foo: *mut Foo);
}

/// 创建包含指定数据的Foo实例,返回指向该实例的原始指针
fn create_foo(data: &[u8]) -> *mut Foo {
    let data_len = data.len();
    // 计算总内存大小:结构体本身大小 + 数据长度 - 1(body已占1字节)
    let total_size = if data_len == 0 {
        mem::size_of::<Foo>()
    } else {
        mem::size_of::<Foo>() + data_len - 1
    };

    // 分配内存,malloc返回的内存满足标准对齐要求
    let foo_ptr = unsafe { malloc(total_size) as *mut Foo };
    if foo_ptr.is_null() {
        panic!("Failed to allocate memory for Foo");
    }

    unsafe {
        // 设置body_len为实际数据长度
        (*foo_ptr).body_len = data_len as u32;
        // 将数据拷贝到body所在的内存区域(包含第一个字节,后续追加剩余数据)
        ptr::copy_nonoverlapping(data.as_ptr(), (*foo_ptr).body.as_mut_ptr(), data_len);
    }

    foo_ptr
}

fn main() {
    // 测试数据
    let test_data = b"Hello from Rust!";
    // 创建Foo实例
    let foo = create_foo(test_data);

    // 调用C函数(必须在unsafe块中,因为涉及原始指针和外部函数)
    unsafe {
        process_foo(foo);
    }

    // 释放内存,避免泄漏
    unsafe {
        free(foo as *mut c_void);
    }
}

关键说明

  • 内存分配:使用libc::malloc分配内存,确保C端可以正常访问和操作;如果用Rust原生的分配器,需要保证对齐方式与C兼容。
  • 数据拷贝:ptr::copy_nonoverlapping用于将Rust切片的数据拷贝到结构体的body区域,由于我们分配了足够的内存,后续的扩展数据会紧跟在结构体之后,C端可以将body转换为unsigned char*直接访问。
  • 安全注意:所有涉及原始指针、外部函数调用的操作都必须放在unsafe块中,开发者需要自行保证内存安全(比如避免空指针、野指针,确保内存释放时机正确)。

内容的提问来源于stack exchange,提问作者constlhq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:51:38