尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Rust core::ffi::c_schar 详解:C `signed char` 类型在 Rust 标准库中的定义与 FFI 使用

Rust core::ffi::c_schar 详解:C `signed char` 类型在 Rust 标准库中的定义与 FFI 使用 Rust core::ffi::c_schar 详解Csigned char类型在 Rust 标准库中的定义与 FFI 使用【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust本文围绕 Rust 标准库中core::ffi模块的c_schar类型展开基于仓库中的官方文档library/core/src/ffi/c_schar.md及其定义源码完整讲解该类型的语义、宏展开机制、稳定性声明、与旧版std::os::raw的兼容关系以及它在 C FFI 场景下的正确用法帮助你在编写extern C接口时精确匹配 C 的signed char类型。一、c_schar 是什么Csigned char的 Rust 等价物仓库文档 library/core/src/ffi/c_schar.md 对该类型的原始定义如下Equivalent to Cssigned chartype.This type will always be [i8], but is included for completeness. It is defined as being a signed integer the same size as a C [char].翻译成中文即c_schar等价于 C 的signed char类型它永远是 [i8]之所以包含它是为了完整性completeness。它的定义是一个与 C [char] 同样大小的有符号整数。这段话包含三个关键事实全部可以在仓库源码中得到印证类型层面它就是i8的别名而不是一个新的整型它的大小恒等于 C 的char在现代架构上即 8 位因为内存是字节寻址的它是无条件固定的i8——这一点与c_char形成鲜明对比后者在不同目标平台上可能是i8也可能是u8详见下文第四节。在 C 语言中char、signed char、unsigned char是三种不同的类型。Rust 用三个对应别名分别覆盖它们c_char、c_schar、c_uchar。如果你的 C 头文件里显式声明了signed char在 Rust 侧就应当使用c_schar而非c_char两者虽然在当前所有支持的目标上大小一致但语义上分属不同的 C 类型混用在 ABI 层面可能引入不必要的歧义。二、源码级定义type_alias!宏如何生成c_scharc_schar的真正定义位于 library/core/src/ffi/primitives.rs全文仅有 20 来行核心代码专门为 FFI 兼容定义了与 C 类型匹配的原始类型别名。文件开头就说明了它存在的目的//! Defines primitive types that match Cs type definitions for FFI compatibility. //! //! This module is intentionally standalone to facilitate parsing when retrieving //! core C types.定义本身由一个名为type_alias的本地宏完成见 library/core/src/ffi/primitives.rs#L6-L16macro_rules! type_alias { { $Docfile:tt, $Alias:ident $Real:ty; $( $Cfg:tt )* } { #[doc include_str!($Docfile)] $( $Cfg )* #[stable(feature core_ffi_c, since 1.64.0)] pub type $Alias $Real; } }这个宏做了三件事也解释了你打开core::ffi文档时看到的文字从哪来#[doc include_str!($Docfile)]把同目录下对应的.md文件内容作为该类型别名的 rustdoc 文档。也就是说c_schar.md不是一个孤立的说明文件而是被编译进文档系统的文档源文件——这就是本文第一节引用的那段英文的来源#[stable(feature core_ffi_c, since 1.64.0)]这批 C 原始类型别名在 Rust 1.64.0 中随 featurecore_ffi_c进入稳定 APIpub type $Alias $Real;展开为一条类型别名声明。c_schar的具体实例化在 library/core/src/ffi/primitives.rs#L23type_alias! { c_schar.md, c_schar i8; }展开后等价于#[doc include_str!(c_schar.md)] #[stable(feature core_ffi_c, since 1.64.0)] pub type c_schar i8;注意这一行没有$( $Cfg )*附加配置项对比同文件中需要按目标平台区分实现的类型如c_char、c_int都带#[doc(cfg(true))]参数可以直观看出c_schar的映射关系在所有目标平台上都是硬编码的i8不存在平台分支。三、公开路径从core::ffi模块到使用者c_schar在 library/core/src/ffi/mod.rs#L30-L35 中被重新导出构成对外的公开 APImod primitives; #[stable(feature core_ffi_c, since 1.64.0)] pub use self::primitives::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, };因此使用方式是core::ffi::c_schar在std环境下通常写作std::ffi::c_schar。core::ffi模块的模块级文档library/core/src/ffi/mod.rs#L1-L7准确概括了这批类型的定位Platform-specific types, as defined by C.Code that interacts via FFI will almost certainly be using the base types provided by C, which arent nearly as nicely defined as Rusts primitive types. This module provides types which will match those defined by C, so that code that interacts with C will refer to the correct types.四、与兄弟类型对照为什么c_schar“永远是 i8”很重要同一宏调用列表library/core/src/ffi/primitives.rs#L21-L38定义了完整的 C 基本类型别名族对照如下别名映射到 Rust 类型是否目标相关c_charc_char_definition::c_chari8或u8是c_schari8否固定c_ucharu8否固定c_short/c_ushorti16/u16否固定c_int/c_uintc_int_definitioni32或i16等是c_long/c_ulongc_long_definition32/64 位平台不同是c_longlong/c_ulonglongi64/u64否固定c_floatf32否固定c_doublec_double_definition个别平台可大于 8 字节是从源码结构看c_char之所以需要平台分支是因为 C 的char是否有符号由目标平台决定primitives.rs中的c_char_definition模块library/core/src/ffi/primitives.rs#L40 起用cfg_select!按target_arch、target_os、target_vendor精确区分了 aarch64、arm、riscv、s390x默认无符号与 Windows、Apple 平台默认有符号等分支。而C 的signed char语义是语言规范中明确无符号的有符号字节类型与char的平台约定无关因此c_schar不需要、也没有任何cfg分支——这正是文档说 This type will always bei8 的底层原因也是它与c_char在需要精确对应 C 头文件时不可互换的原因。五、向后兼容std::os::raw::c_schar的等价别名在 Rust 早期版本中这类 C 类型别名存放在std::os::raw模块下。仓库通过兼容性层保证两者完全等价见 library/std/src/os/raw/mod.rs#L8-L26macro_rules! alias_core_ffi { ($($t:ident)*) {$( #[stable(feature raw_os, since 1.1.0)] #[doc include_str!(concat!(../../../../core/src/ffi/, stringify!($t), .md))] #[doc(cfg(all()))] pub type $t core::ffi::$t; )*} } alias_core_ffi! { c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong c_float c_double c_void }这里有两个值得注意的细节std::os::raw中的每个别名直接指向core::ffi中同名类型例如pub type c_schar core::ffi::c_schar;即最终都是同一个i8别名旧模块的文档复用了 core 侧的同一份.md文件include_str!(concat!(../../../../core/src/ffi/, stringify!($t), .md))保证core::ffi::c_schar与std::os::raw::c_schar的文档文字永远一致。该兼容层自 Rust 1.1.0featureraw_os起稳定。仓库还提供了类型级测试来验证等价性。library/std/src/os/raw/tests.rs#L12-L17 断言标准库别名与libccrate 中同名类型的TypeId完全相同#[test] fn same() { use crate::os::raw; ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong c_float c_double); }其中ok!宏的展开是assert!(TypeId::of::libc::$t() TypeId::of::raw::$t(), ...)。从源码结构看该测试在 MSVC 目标上被排除文件首行#![cfg(not(all(windows, target_env msvc)))]因为 MSVC 的 32 位 ABI 与libccrate 在其他目标上的约定存在差异——这也侧面印证FFI 类型别名的正确性是需要按目标逐一守护的。六、实战在extern C声明中正确使用 c_schar结合以上定义典型的 FFI 使用方式如下。假设 C 侧头文件为// header.h signed char clamp_byte(signed char value); unsigned char clamp_byte_abs(void); char first_letter(void);对应的 Rust 绑定应当为use core::ffi::{c_char, c_schar, c_uchar}; extern C { fn clamp_byte(value: c_schar) - c_schar; // 对应 signed char fn clamp_byte_abs() - c_uchar; // 对应 unsigned char fn first_letter() - c_char; // 对应 char } fn main() { // c_schar 就是 i8可以直接与 i8 字面量互换 let v: c_schar -1; let r unsafe { clamp_byte(v) }; assert!(r i8::MIN r i8::MAX); }要点提示c_schar与i8是同一类型别名可以直接赋值、比较、做范围运算无需任何转换函数在extern C块中请按 C 头文件的显式写法选择类型头文件写signed char就用c_schar写unsigned char就用c_uchar裸写char才用c_char如果你只是需要一个 8 位有符号字节而不涉及 C 互操作直接用i8即可c_schar的价值在于语义上声明“这个参数来自 C 的signed char”让 ABI 意图显式化。七、如何复核本文涉及的实现所有结论均可在当前仓库内直接查证关键入口文档源文件library/core/src/ffi/c_schar.md类型别名定义与宏展开library/core/src/ffi/primitives.rs模块级重导出library/core/src/ffi/mod.rsstd::os::raw兼容层library/std/src/os/raw/mod.rs类型等价性测试library/std/src/os/raw/tests.rs小结core::ffi::c_schar是 Rust 标准库为 C 互操作提供的signed char等价类型它由type_alias!宏生成恒等于i8自 1.64.0 起稳定并通过std::os::raw保留了自 1.1.0 起的旧路径。理解“c_schar永远固定而c_char随平台变化”这一区别是编写与 C 头文件精确对应、且跨平台稳定的 FFI 绑定的关键一步。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表