当前位置: 首页 > news >正文

Rust:开发 DLL 动态链接库时如何处理 C 字符串

在 Rust 开发 DLL 时,正确处理 *mut c_char*const c_charString 的转换非常重要。以下是几种常见场景的安全转换方法:


1️⃣ C 字符串 → Rust String

将传入的 *const c_char (C 风格以 null 结尾的字符串) 转换为 Rust 的 String

use std::ffi::CStr;
use std::os::raw::c_char;#[no_mangle]
pub extern "C" fn process_c_string(c_string_ptr: *const c_char) {// 安全转换let rust_str = unsafe {// 注意:要求传入有效的 C 字符串指针 (null-terminated)CStr::from_ptr(c_string_ptr).to_str()         // 转换为 &str.expect("Invalid UTF-8") // 处理错误.to_string()      // &str → String};println!("Rust received: {}", rust_str);
}

安全要点

  • 使用 CStr::from_ptr 包装 C 字符串
  • to_str() 处理 UTF-8 转换(可能会失败)
  • 必须进行错误处理(这里用 expect(),生产环境建议更健壮的方案)

2️⃣ Rust String → C 字符串

将 Rust String 转换为可返回给 C 的 *mut c_char

use std::ffi::CString;#[no_mangle]
pub extern "C" fn generate_c_string() -> *mut c_char {let rust_string = "从 Rust 生成的字符串".to_string();// 创建 CString (可能失败)let c_string = CString::new(rust_string).expect("Failed to create CString");// 泄漏所有权,转为原始指针c_string.into_raw()
}// ⚠️ 必须提供对应的内存释放函数!
#[no_mangle]
pub extern "C" fn free_c_string(ptr: *mut c_char) {unsafe {if !ptr.is_null() {// 回收所有权并自动释放内存let _ = CString::from_raw(ptr);}}
}

关键点

  • into_raw() 让 Rust 放弃所有权,C 代码需负责管理内存
  • 必须提供对应的 free_xxx 函数
  • 注意字符串中的 null 字符会导致 CString::new() 失败

3️⃣ 完整 DLL 示例

结合转换的综合使用:

// src/lib.rs
use std::ffi::{CStr, CString};
use std::os::raw::c_char;#[no_mangle]
pub extern "C" fn modify_string(input: *const c_char) -> *mut c_char {// Step 1: C字符串 → Rust Stringlet rust_str = unsafe {CStr::from_ptr(input).to_str().unwrap_or_default()  // 更安全的错误处理.to_uppercase()       // 处理字符串};// Step 2: Rust String → C字符串CString::new(rust_str).map(|s| s.into_raw()).unwrap_or(std::ptr::null_mut())
}#[no_mangle]
pub extern "C" fn free_modified_string(ptr: *mut c_char) {if !ptr.is_null() {unsafe { let _ = CString::from_raw(ptr); }}
}

4️⃣ C/C++ 调用示例

如何在 C 中正确使用 DLL:

#include <stdio.h>
#include <windows.h>typedef char* (*fn_modify_string)(const char*);
typedef void (*fn_free_string)(char*);int main() {HINSTANCE dll = LoadLibrary("rust_dll.dll");if (!dll) return 1;fn_modify_string modify = (fn_modify_string)GetProcAddress(dll, "modify_string");fn_free_string free_str = (fn_free_string)GetProcAddress(dll, "free_modified_string");// 调用函数char* result = modify("hello from c");printf("Modified: %s\n", result);// 必须释放内存!free_str(result);FreeLibrary(dll);return 0;
}

🧠 高级处理技巧

1. 处理无效指针和错误
pub extern "C" fn safe_convert(ptr: *const c_char) -> Option<String> {if ptr.is_null() {return None;}unsafe {CStr::from_ptr(ptr).to_str().ok().map(|s| s.to_string())}
}
2. 多字节编码转换 (Windows)
use encoding_rs::WINDOWS_1252;pub fn to_wide_string(s: &str) -> Vec<u16> {let (cow, _) = WINDOWS_1252.encode(s);cow.iter().map(|b| *b as u16) // 单字节转宽字符.chain(std::iter::once(0)) // 添加 null 终止符.collect()
}
3. 返回复杂数据结构
#[repr(C)]
pub struct StringResult {data: *mut c_char,success: bool
}#[no_mangle]
pub extern "C" fn complex_operation() -> StringResult {// ... 操作 ...match result {Ok(s) => StringResult {data: CString::new(s).unwrap().into_raw(),success: true},Err(_) => StringResult {data: std::ptr::null_mut(),success: false}}
}

⚠️ 关键安全警告

  1. 线程安全

    use std::sync::Mutex;static BUFFER: Mutex<String> = Mutex::new(String::new());
    
  2. 空指针检查

    • 所有指针参数必须 is_null() 检查
    • 返回指针要考虑无效情况
  3. 禁止的字符串转换

    // 绝对禁止这种转换!
    unsafe { std::str::from_utf8_unchecked(slice) }
    
  4. 内存生命周期

    // 危险:局部变量内存被释放!
    pub extern "C" fn invalid_example() -> *const c_char {let s = String::from("temporary");CString::new(s).unwrap().as_ptr() // 返回即将销毁的指针
    }
    

💡 最佳实践总结

  1. 使用 CStr / CString 进行安全的转换
  2. 明确所有权
    • into_raw() 转让所有权到 C
    • from_raw() 取回所有权释放内存
  3. 配套释放函数
    • 每个 into_raw() 必须有对应的 free 函数
  4. 错误处理
    • 返回 Option 或带错误标识的结构体
  5. 测试边界情况
    • 空指针
    • 无效 UTF-8
    • 含 null 的字符串

完成开发后,建议使用 cbindgen 自动生成头文件:

cargo install cbindgen
cbindgen --lang c --output mydll.h
http://www.xdnf.cn/news/1236259.html

相关文章:

  • GaussDB SQL执行计划详解
  • Flutter各大主流状态管理框架技术选型分析及具体使用步骤
  • RAG-Semantic Chunking
  • 一加Ace5无法连接ColorOS助手解决(安卓设备ADB模式无法连接)
  • 迈向透明人工智能: 可解释性大语言模型研究综述
  • JavaScript 性能优化实战指南:从运行时到用户体验的全面提升​
  • LangGraph认知篇-Persistence 持久化
  • 嵌入式学习日志——数据结构(一)
  • Supergateway教程
  • 使用DrissionPage实现xhs笔记自动翻页并爬取笔记视频、图片
  • Day22--回溯--77. 组合,216. 组合总和 III,17. 电话号码的字母组合
  • Kafka 是什么?
  • 《汇编语言:基于X86处理器》第11章 MS-Windows编程(3)
  • 【stm32】按键控制LED以及光敏传感器控制蜂鸣器
  • OSPF知识点整理
  • 实战《从0开始使用SwiftUI搭建记账软件》- 2、SwiftUI 知识点详解与使用场景
  • 6.1、Redis多级缓存原理和优化、Redis部分参数优化调整
  • 【超分辨率专题】PiSA-SR:单步Diff超分新突破,即快又好,还能在线调参
  • Linux 摄像头实时抓取:V4L2、FFmpeg 与 GStreamer 全面讲解
  • python工具方法51 视频数据的扩充(翻转、resize、crop、re_fps)
  • Transformer模型用于MT信号相关性预测与分析
  • 《深入浅出RabbitMQ:从零基础到面试通关》
  • 渗透作业4
  • wordpress登陆前登陆后显示不同的顶部菜单
  • 数据结构代码
  • 08.Redis 持久化
  • AOP动态代理
  • #C语言——刷题攻略:牛客编程入门训练(四):运算
  • 大屏项目展示
  • 面向智能体的上下文工程:策略、实现与 LangGraph 实践