国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

從 Rust 調用 C 庫函數

開發
Rust FFI 和 bindgen 工具是為 Rust 調用 C 庫而設計的。Rust 很容易與 C 語言對話,從而與任何其它可以與 C 語言對話的語言對話。

為什么要從 Rust 調用 C 函數?簡短的答案就是軟件庫。冗長的答案則觸及到 C 在眾多編程語言中的地位,特別是相對 Rust 而言。C、C++,還有 Rust 都是系統語言,這意味著程序員可以訪問機器層面的數據類型與操作。在這三個系統語言中,C 依然占據主導地位。現代操作系統的內核主要是用 C 來寫的,其余部分依靠匯編語言補充。在標準系統函數庫中,輸入與輸出、數字處理、加密計算、安全、網絡、國際化、字符串處理、內存管理等等,大多都是用 C 來寫的。這些函數庫所代表的是一個龐大的基礎設施,支撐著用其他語言寫出來的應用。Rust 發展至今也有著可觀的函數庫,但是 C 的函數庫 —— 自 1970 年代就已存在,迄今還在蓬勃發展 —— 是一種無法被忽視的資源。最后一點是,C 依然還是編程語言中的 ??通用語??:大部分語言都可以與 C 交流,透過 C,語言之間可以互相交流。

兩個概念證明的例子

Rust 支持 FFI(外部函數接口Foreign Function Interface)用以調用 C 函數。任何 FFI 所需要面臨的問題是調用方語言是否涵蓋了被調用語言的數據類型。例如,??ctypes??? 是 Python 調用 C 的 FFI,但是 Python 并沒有包括 C 所支持的無符號整數類型。結果就是,??ctypes?? 必須尋求解決方案。

相比之下,Rust 包含了所有 C 中的原始(即,機器層面)類型。比如說,Rust 中的 ??i32??? 類對應 C 中的 ??int??? 類。C 特別聲明了 ??char??? 類必須是一個字節大小,而其他類型,比如 ??int???,必須至少是這個大小(LCTT 譯注:原文處有評論指出 ??int??? 大小依照 C 標準應至少為 2 字節);然而如今所有合理的 C 編譯器都支持四字節的 ??int???,以及八字節的 ??double???(Rust 中則是 ??f64?? 類),以此類推。

針對 C 的 FFI 所面臨的另一個挑戰是:FFI 是否能夠處理 C 的裸指針,包括指向被看作是字符串的數組指針。C 沒有字符串類型,它通過結合字符組和一個非打印終止符(大名鼎鼎的 空終止符)來實現字符串。相比之下,Rust 有兩個字符串類型:??String??? 和 ??&str?? (字符串切片)。問題是,Rust FFI 是否能將 C 字符串轉化成 Rust 字符串——答案是 肯定的

出于對效率的追求,結構體指針在 C 中也很常見。一個 C 結構體在作為一個函數的參數或者返回值的時候,其默認行為是傳遞值(即,逐字節復制)。C 結構體,如同它在 Rust 中的對應部分一樣,可以包含數組和嵌套其他結構體,所以其大小是不定的。結構體在兩種語言中的最佳用法是傳遞或返回引用,也就是說,傳遞或返回結構體的地址而不是結構體本身的副本。Rust FFI 再一次成功處理了 C 的結構體指針,其在 C 函數庫中十分普遍。

第一段代碼案例專注于調用相對簡單的 C 庫函數,比如 ??abs???(絕對值)和 ??sqrt???(平方根)。這些函數使用非指針標量參數并返回一個非指針標量值。第二段代碼案例則涉及了字符串和結構體指針,在這里會介紹工具 ??bindgen???,其通過 C 接口(頭文件)生成 Rust 代碼,比如 ??math.h??? 以及 ??time.h???。C 頭文件聲明了 C 函數的調用語法,并定義了會被調用的結構體。兩段代碼都能在 ??我的主頁上?? 找到。

調用相對簡單的 C 函數

第一段代碼案例有四處 Rust 對標準數學庫內的 C 函數的調用:兩處分別調用了 ??abs???(絕對值)和 ??pow???(冪),兩處重復調用了 ??sqrt???(平方根)。這個程序可以直接用 ??rustc??? 編譯器進行構建,或者使用更方便的命令 ??cargo build??:

    use std::os::raw::c_int;  // 32位
use std::os::raw::c_double; // 64位
// 從標準庫 libc 中引入三個函數。
// 此處是 Rust 對三個 C 函數的聲明:
extern "C" {
fn abs(num: c_int) -> c_int;
fn sqrt(num: c_double) -> c_double;
fn pow(num: c_double, power: c_double) -> c_double;
}
fn main() {
let x: i32 = -123;
println!("\n{x}的絕對值是: {}.", unsafe { abs(x) });
let n: f64 = 9.0;
let p: f64 = 3.0;
println!("\n{n}的{p}次方是: {}.", unsafe { pow(n, p) });
let mut y: f64 = 64.0;
println!("\n{y}的平方根是: {}.", unsafe { sqrt(y) });
y = -3.14;
println!("\n{y}的平方根是: {}.", unsafe { sqrt(y) }); //** NaN = NotaNumber(不是數字)
}

頂部的兩個 ??use??? 聲明是 Rust 的數據類型 ??c_int??? 和 ??c_double???,對應 C 類型里的 ??int??? 和 ??double???。Rust 標準模塊 ??std::os::raw??? 定義了 14 個類似的類型以確保跟 C 的兼容性。模塊 ??std::ffi?? 中有 14 個同樣的類型定義,以及對字符串的支持。

位于 ??main??? 函數上的 ??extern "C"??? 區域聲明了 3 個 C 庫函數,這些函數會在 ??main??? 函數內被調用。每次調用都使用了標準的 C 函數名,但每次調用都必須發生在一個 ??unsafe??? 區域內。正如每個新接觸 Rust 的程序員所發現的那樣,Rust 編譯器極度強制內存安全。其他語言(特別是 C 和 C++)作不出相同的保證。??unsafe?? 區域其實是說:Rust 對外部調用中可能存在的不安全行為不負責。

第一個程序輸出為:

    -123的絕對值是: 123.
93次方是: 729.
64的平方根是: 8.
-3.14的平方根是: NaN.

輸出的最后一行的 ??NaN??? 表示不是數字Not a Number:C 庫函數 ??sqrt??? 期待一個非負值作為參數,這使得參數 ??-3.14??? 生成了 ??NaN?? 作為返回值。

調用涉及指針的 C 函數

C 庫函數為了提高效率,經常在安全、網絡、字符串處理、內存管理,以及其他領域中使用指針。例如,庫函數 ??asctime???(ASCII 字符串形式的時間)期待一個結構體指針作為其參數。Rust 調用類似 ??asctime??? 的 C 函數就會比調用 ??sqrt?? 要更加棘手一些,后者既沒有牽扯到指針,也不涉及到結構體。

函數 ??asctime??? 調用的 C 結構體類型為 ??struct tm???。一個指向此結構體的指針會作為參數被傳遞給庫函數 ??mktime???(時間作為值)。此結構體會將時間拆分成諸如年、月、小時之類的單位。此結構體的字段field類型為 ??time_t???,是 ??int???(32位)和 ??long???(64 位)的別名。兩個庫函數將這些破碎的時間片段組合成了一個單一值:??asctime??? 返回一個以字符串表示的時間,而 ??mktime??? 返回一個 ??time_t??? 值表示自 “??紀元??Epoch

以下的 C 程序調用了 ??asctime??? 和 ??mktime???,并使用了其他庫函數 ??strftime??? 來將 ??mktime?? 的返回值轉化成一個格式化的字符串。這個程序可被視作 Rust 對應版本的預熱:

    #include <stdio.h>
#include <time.h>
int main () {
struct tm sometime; /* 時間被打破細分 */
char buffer[80];
int utc;
sometime.tm_sec = 1;
sometime.tm_min = 1;
sometime.tm_hour = 1;
sometime.tm_mday = 1;
sometime.tm_mon = 1;
sometime.tm_year = 1; /*LCTT 校注:注意,相對于 1900 年的年數*/
sometime.tm_hour = 1;
sometime.tm_wday = 1;
sometime.tm_yday = 1;
printf("日期與時間: %s\n", asctime(&sometime));
utc = mktime(&sometime);
if( utc < 0 ) {
fprintf(stderr, "錯誤: mktime 無法生成時間\n");
} else {
printf("返回的整數值: %d\n", utc);
strftime(buffer, sizeof(buffer), "%c", &sometime);
printf("更加可讀的版本: %s\n", buffer);
}
return 0;
}

程序輸出為:

    日期與時間: Fri Feb  1 01:01:01 1901
返回的整數值: 2120218157
更加可讀的版本: Fri Feb 1 01:01:01 1901

(LCTT 譯注:如果你嘗試在自己電腦上運行這段代碼,然后得到了一行關于 ??mktime??? 的錯誤信息,然后又在網上隨便找了個在線 C 編譯器,復制代碼然后得到了跟這里的結果有區別但是沒有錯誤的結果,不要慌,我的電腦上也是這樣的。導致本地機器上 ??mktime??? 失敗的原因是作者沒有設置 ??tm_isdst???,這個是用來標記夏令時的標志。??tm_isdst???。加入 ??sometime.tm_isdst = 0??? 或 ??= -1??? 后應該就能得到跟在線編譯器大致一樣的結果。不同的地方在于結果第一行我得到的是 ??Mon Feb ...???,這個與作者代碼中 ??sometime.tm_wday = 1?? 對應,這里應該是作者寫錯了;第二行我和作者和網上得到的數字都不一樣,這大概是合理的,因為這與機器的紀元有關;第三行我跟作者的結果是一樣的,1901 年 2 月 1 日也確實是周五,這是因為 ??mktime???。至于夏令時具體是如何影響 ??mktime??? 這個問題,我能查到的只有 ??mktime?? 的計算受時區影響,更底層的原因我也不知道了。)

總的來說,Rust 在調用庫函數 ??asctime??? 和 ??mktime?? 時,必須處理以下兩個問題:

  • 將裸指針作為唯一參數傳遞給每個庫函數。
  • 把從??asctime?? 返回的 C 字符串轉化為 Rust 字符串。

Rust 調用 asctime 和 mktime

工具 ??bindgen??? 會根據類似 ??math.h??? 和 ??time.h??? 之類的 C 頭文件生成 Rust 支持的代碼。下面這個簡化版的 ??time.h?? 就可以用來做例子,簡化版與原版主要有兩個不同:

  • 內置類型??int??? 被用來取代別名類型??time_t???。工具 bindgen 可以處理??time_t??? 類,但是會生成一些煩人的警告,因為??time_t??? 不符合 Rust 的命名規范:??time_t??? 以下劃線區分??time??? 和??t???;Rust 更偏好駝峰式命名方法,比如??TimeT??。
  • 出于同樣的原因,這里選擇??StructTM??? 作為??struct tm?? 的別名。

以下是一份簡化版的頭文件,??mktime??? 和 ??asctime?? 在文件底部:

    typedef struct tm {
int tm_sec; /* 秒 */
int tm_min; /* 分鐘 */
int tm_hour; /* 小時 */
int tm_mday; /* 日 */
int tm_mon; /* 月 */
int tm_year; /* 年 */
int tm_wday; /* 星期 */
int tm_yday; /* 一年中的第幾天 */
int tm_isdst; /* 夏令時 */
} StructTM;
extern int mktime(StructTM*);
extern char* asctime(StructTM*);

??bindgen??? 安裝好后,??mytime.h??? 作為以上提到的頭文件,以下命令(??%??? 是命令行提示符)可以生成所需的 Rust 代碼并將其保存到文件 ??mytime.rs??:

% bindgen mytime.h > mytime.rs

以下是 ??mytime.rs?? 中的重要部分:

    /* automatically generated by rust-bindgen 0.61.0 */
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct tm {
pub tm_sec: ::std::os::raw::c_int,
pub tm_min: ::std::os::raw::c_int,
pub tm_hour: ::std::os::raw::c_int,
pub tm_mday: ::std::os::raw::c_int,
pub tm_mon: ::std::os::raw::c_int,
pub tm_year: ::std::os::raw::c_int,
pub tm_wday: ::std::os::raw::c_int,
pub tm_yday: ::std::os::raw::c_int,
pub tm_isdst: ::std::os::raw::c_int,
}
pub type StructTM = tm;
extern "C" {
pub fn mktime(arg1: *mut StructTM) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn asctime(arg1: *mut StructTM) -> *mut ::std::os::raw::c_char;
}
#[test]
fn bindgen_test_layout_tm() {
const UNINIT: ::std::mem::MaybeUninit<tm> = ::std::mem::MaybeUninit::uninit();
let ptr = UNINIT.as_ptr();
assert_eq!(
::std::mem::size_of::<tm>(),
36usize,
concat!("Size of: ", stringify!(tm))
);
...

Rust 結構體 ??struct tm???,跟原本在 C 中的一樣,包含了 9 個 4 字節的整型字段。這些字段名稱在 C 和 Rust 中是一樣的。??extern "C"??? 區域聲明了庫函數 ??astime??? 和 ??mktime??? 分別需要只一個參數,一個指向可變實例 ??StructTM?? 的裸指針。(庫函數可能會通過指針改變作為參數傳遞的結構體。)

??#[test]??? 屬性下的其余代碼是用來測試 Rust 版的時間結構體的布局。通過命令 ??cargo test??? 可以進行這些測試。問題在于,C 沒有規定編譯器應該如何對結構體中的字段進行布局。比如說,C 的 ??struct tm??? 以字段 ??tm_sec?? 開頭用以表示秒;但是 C 不需要編譯版本遵循這個排序。不管怎樣,Rust 測試應該會成功,而 Rust 對庫函數的調用也應如預期般工作。

設置好第二個案例并開始運行

從 ??bindgen??? 生成的代碼不包含 ??main??? 函數,所以是一個天然的模塊。以下是一個 ??main??? 函數初始化了 ??StructTM??? 并調用了 ??asctime??? 和 ??mktime??:

    mod mytime;
use mytime::*;
use std::ffi::CStr;
fn main() {
let mut sometime = StructTM {
tm_year: 1,
tm_mon: 1,
tm_mday: 1,
tm_hour: 1,
tm_min: 1,
tm_sec: 1,
tm_isdst: -1,
tm_wday: 1,
tm_yday: 1
};
unsafe {
let c_ptr = &mut sometime; // 裸指針
// 調用,轉化,并擁有
// 返回的 C 字符串
let char_ptr = asctime(c_ptr);
let c_str = CStr::from_ptr(char_ptr);
println!("{:#?}", c_str.to_str());
let utc = mktime(c_ptr);
println!("{}", utc);
}
}

這段 Rust 代碼可以被編譯(直接用 ??rustc??? 或使用 ??cargo??)并運行。輸出為:

    Ok(
"Mon Feb 1 01:01:01 1901\n",
)
2120218157

對 C 函數 ??asctime??? 和 ??mktime??? 的調用必須再一次被放在 ??unsafe??? 區域內,因為 Rust 編譯器無法對這些外部函數的潛在內存安全風險負責。此處聲明一下,??asctime??? 和 ??mktime??? 并沒有安全風險。調用的兩個函數的參數是裸指針 ??ptr???,其指向結構體 ??sometime?? (在棧stack中)的地址。

??asctime??? 是兩個函數中調用起來更棘手的那個,因為這個函數返回的是一個指向 C ??char??? 的指針,如果函數返回 ??Mon??? 那么指針就指向 ??M???。但是 Rust 編譯器并不知道 C 字符串 (??char??? 的空終止數組)的儲存位置。是內存里的靜態空間?還是堆heap???asctime?? 函數內用來儲存時間的文字表達的數組實際上是在內存的靜態空間里。無論如何,C 到 Rust 字符串轉化需要兩個步驟來避免編譯錯誤:

  • 調用??Cstr::from_ptr(char_ptr)??? 來將 C 字符串轉化為 Rust 字符串并返回一個引用儲存在變量??c_str?? 中。
  • 對??c_str.to_str()??? 的調用確保了??c_str?? 是所有者。

Rust 代碼不會增加從 ??mktime??? 返回的整型值的易讀性,這一部分留作課外作業給感興趣的人去探究。Rust 模板 ??chrono::format??? 也有一個 ??strftime?? 函數,它可以被當作 C 的同名函數來使用,兩者都是獲取時間的文字表達。

使用 FFI 和 bindgen 調用 C

Rust FFI 和工具 ??bindgen??? 都能夠出色地協助 Rust 調用 C 庫,無論是標準庫還是第三方庫。Rust 可以輕松地與 C 交流,并透過 C 與其他語言交流。對于調用像 ??sqrt?? 一樣簡單的庫函數,Rust FFI 表現直截了當,這是因為 Rust 的原始數據類型覆蓋了它們在 C 中的對應部分。

對于更為復雜的交流 —— 特別是 Rust 調用像 ??asctime??? 和 ??mktime??? 一樣,會涉及到結構體和指針的 C 庫函數 —— ??bindgen??? 工具是優秀的幫手。這個工具會生成支持代碼以及所需要的測試。當然,Rust 編譯器無法假設 C 代碼對內存安全的考慮會符合 Rust 的標準;因此,Rust 必須在 ??unsafe?? 區域內調用 C。

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2022-08-05 13:38:08

C語言庫函數printf()

2017-08-03 08:34:54

gRPCCRust

2010-01-13 11:10:17

C++庫函數

2010-01-28 13:35:41

調用C++函數

2010-06-13 10:18:08

MySQL 數據庫函數

2009-12-08 11:10:20

PHP GD庫函數

2022-03-25 09:57:18

C++Rust語言

2021-07-30 07:05:31

C語言Memcpy Memmove

2009-08-03 13:34:57

C#調用Windows

2009-08-14 09:15:28

C#調用構造函數

2009-08-25 10:59:00

C#調用函數顯示值

2009-07-31 16:12:10

Windows APIC#

2010-01-21 11:23:58

C++函數調用

2009-08-07 17:22:36

C#調用dll導出函數

2023-05-29 16:25:59

Rust函數

2009-12-01 15:14:32

PHP Substr庫

2010-06-17 13:16:07

SQLServer 數

2010-11-29 10:36:18

Sybase數據庫函數

2009-08-05 09:40:02

C#調用DLL函數

2009-08-05 09:30:39

C#調用DLL函數
點贊
收藏

51CTO技術棧公眾號

99热这里只有精品免费| 欧美激情国产精品日韩| 国产在线观看91一区二区三区 | 欧美刺激性大交免费视频| 国产精品不卡在线观看| 欧美另类高清视频在线| 疯狂做受xxxx欧美肥白少妇| 欧美成熟毛茸茸| 亚洲人成在线网站| 狠狠色综合网站久久久久久久| 美女毛片一区二区三区四区| 日韩欧美国产成人一区二区| 麻豆av一区| 好吊一区二区三区| 不卡的av电影在线观看| 宅男深夜国产| 香港成人在线视频| 僵尸再翻生在线观看| 美女久久久久久久久久久| 神马电影网我不卡| 精品成人乱色一区二区| 男女啪啪免费视频网站| 午夜日韩av| 国产精品久久久久久久app| 国产欧美成人| 成人在线观看黄| 色综合久久中文综合久久牛| 97成人在线免费视频| 丁香激情综合国产| 欧美伦理片在线看| 国产女同性恋一区二区| 97超碰人人在线| 一区二区三区国产视频| 操人在线观看| 亚洲黄页视频免费观看| 精品久久久网| 91香蕉嫩草影院入口| 日韩av一区二区在线影视| 四虎国产成人永久精品免费| 91精品国产91久久久久久一区二区| 久久99精品久久| 久久影视电视剧免费网站清宫辞电视 | 欧美性猛交xxxx黑人交| 黄色网址在线免费| 欧美中文字幕在线观看| 激情婷婷久久| 成人高清dvd| ww国产内射精品后入国产| 中文字幕字幕中文在线中不卡视频| 黄色一级视频网站| 欧美一区二区三区影视| 精品国产伦一区二区三区观看说明 | 欧美在线免费播放| 日本一区二区在线视频| gogogo免费视频观看亚洲一| 日本免费看黄色| 91精品国产一区二区三区| 欧美精选视频一区二区| 日本伊人精品一区二区三区介绍| 久久xxxx精品视频| 免费男女羞羞的视频网站中文版 | 污视频网站在线观看| 亚洲第一视频网站| 精品国产精品| 久久国产视频网| 电影91久久久| 性生活免费在线观看| 国产精品综合不卡av| 韩国精品在线观看 | 日本免费在线观看| 日韩小视频在线观看| 欧美区日韩区| 91热这里只有精品| 精品日韩在线一区| 青青草国产成人a∨下载安卓| 亚洲在线不卡| 狠狠色噜噜狠狠狠狠97| 国产精品美女久久久久人| 你懂的视频在线一区二区| 亚洲日本va在线观看| 松下纱荣子在线观看| 在线看福利影| 国产精品自在线| 国产成人免费av在线| 国产精品一区在线看| 欧美精品videosex性欧美| 精品一区二区三区香蕉蜜桃| 国产在线视频福利| 91精品国产91久久久久久最新| 蜜臀av一级做a爰片久久| 欧美女优在线| 日本伊人精品一区二区三区介绍 | 免费亚洲一区二区| 香蕉影视欧美成人| 伊人久久影院| 精品人妻人人做人人爽| 欧美一级欧美三级| 男女羞羞在线观看| 中文av资源在线| 大桥未久一区二区三区| 色噜噜国产精品视频一区二区| 国产一区二区精品久久99| 香蕉网站在线| 精华区一区二区三区| 国产一区在线免费观看| 色噜噜狠狠狠综合曰曰曰| 久久久国产精品麻豆| 九九久久成人| 搞黄网站在线观看| 亚洲精品一区二| 亚洲国产日韩欧美在线图片| 日韩成人一区二区| 欧美gay视频| 涩涩漫画在线观看| 久久伊人资源站| 欧美精品久久久久久久久久| 色综合夜色一区| 久久天堂精品| 一区二区三区福利| 樱桃成人精品视频在线播放| 老司机精品久久| 国产一二精品视频| 国产精品国产a级| 91久久精品国产91性色tv| 精品免费视频一区二区| 中文字幕亚洲情99在线| 国产一区欧美二区三区| 久操手机在线视频| 四虎影院在线域名免费观看| 最新日韩精品| 91精品亚洲| 国产色婷婷亚洲99精品小说| 一本色道久久综合亚洲精品按摩 | 国产理论电影在线| 成人免费直播在线| 久久久xxx| 亚洲午夜电影在线观看| 亚洲无限av看| 国产精品一区二区欧美| 性欧美videossex精品| 川上优av中文字幕一区二区| 久久久久国产精品午夜一区| 欧美性色黄大片手机版| 91国产精品91| 日本亚洲天堂| 国产日韩欧美一区二区三区| 久久久久国产精品麻豆ai换脸| 亚洲成人精品久久| 国模一区二区三区私拍视频| 亚洲精品无码久久久久久| 欧美成a人片在线观看久| 免费成人av在线| 欧美一区二区三区四区久久| 国产一区二区黄色| 精华区一区二区三区| 色777狠狠狠综合伊人| 亚洲成人av免费| 国产精品成人在线| 日本视频三区| 日韩欧美一区二区三区在线视频| 亚洲精品视频免费看| 国产精品网站大全| 日本免费在线视频| 日韩电影在线观看网站| 亚洲视频999| 人人妻人人澡人人爽欧美一区双| 91精品专区| 成人精品影院| 国产精品av在线| 99热这里只有精品在线播放| 校园春色亚洲色图| 激情欧美一区二区| 国产在线视频精品一区| 亚洲精品乱码久久久久久按摩观| 成人春色激情网| 亚洲精品福利免费在线观看| 欧美一级欧美三级在线观看| 欧美日韩专区在线| 日韩一级完整毛片| 中文字幕国内精品| 5252色成人免费视频| 久久国产欧美精品| 国产一区一区三区| 日本在线аv| 男女网站在线观看| 日韩免费小视频| 久久国产亚洲| 国产精品色悠悠| 亚洲一区二区三区香蕉| 亚洲社区在线| 人妻久久久一区二区三区| 激情亚洲综合在线| 亚洲欧美在线综合| 久久99国内| 日韩视频在线你懂得| 日本欧洲一区| 国产一区二区三区不卡在线| 国产午夜精品一区在线观看| 久久久久蜜桃| 一区二区三区在线视频免费观看|