[英]Inconveniences of using uint64_t
我有一個高度可移植的庫(即使沒有內核,它也可以在任何地方編譯和運行良好),我希望它盡可能保持可移植性。 到目前為止,我已經避免使用 64 位數據類型,但我現在可能需要使用它們——准確地說,我需要一個 64 位位掩碼。
我從來沒有真正考慮過,我還不夠硬件專家(尤其是在嵌入式系統方面),但我現在想知道:使用uint64_t
(或等效地, uint_least64_t
)有哪些不便之uint_least64_t
? 我可以想到兩種方法來解決我的問題:
對符合標准的 C 編譯器有各種最低要求。 C 語言允許兩種形式的編譯器:托管和獨立。 Hosted 旨在運行在 OS 之上,而獨立運行則無需 OS。 大多數嵌入式系統編譯器都是獨立的實現。
獨立編譯器有一些回旋余地,它們不需要支持所有標准庫,但需要支持它們的最小子集。 這包括stdint.h
(參見 C17 4/6)。 這反過來又要求編譯器實現以下內容(C17 7.20.1.2/3):
需要以下類型:
int_least8_t int_least16_t int_least32_t int_least64_t
uint_least8_t uint_least16_t uint_least32_t uint_least64_t
所以微控制器編譯器不需要支持uint64_t
,但它必須(奇怪的是)支持uint_least64_t
。 實際上,這意味着編譯器也可以添加uint64_t
支持,因為在這種情況下它是相同的。
至於 8 位 MCU 支持什么……它通過指令集支持 8 位算術,在某些特殊情況下還支持一些使用索引寄存器的 16 位運算。 但一般來說,每當使用大於 8 位的類型時,它都必須依賴軟件庫。
所以如果你在 8bit 上嘗試 32 位算術,它會將一些編譯器軟件庫與代碼內聯,結果將是數百條匯編指令,使得這些代碼非常低效和消耗內存。 64位會更糟。
與缺乏 FPU 的 MCU 上的浮點數相同,這些也會通過軟件浮點庫生成極其低效的代碼。
為了說明這一點,請看一下在 8 位 AVR (gcc) 上進行一些非常簡單的 64 位加法的未優化代碼: https : //godbolt.org/z/ezbKjY
它實際上支持uint64_t
但編譯器產生了大量的開銷代碼,大約 100 條指令。 在它中間,調用了隱藏在可執行文件中的內部編譯器函數call __adddi3
。
如果我們啟用優化,我們會得到
add64:
push r10
push r11
push r12
push r13
push r14
push r15
push r16
push r17
call __adddi3
pop r17
pop r16
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
ret
我們將不得不深入挖掘庫源或單步執行程序集,以查看__adddi3
有多少代碼。 我猜它仍然不是一個微不足道的功能。
因此,正如您所希望的那樣,在 8 位 CPU 上執行 64 位算術是一個非常糟糕的主意。
好吧,如果您主要關心的是保持相當水平的兼容性,這就是避免使用 64 位數字的原因,為什么不使用int
整數數組,並考慮使用一個完整的整數來存儲,比如說,30位。
我建議您查看有關使用位掩碼(大於 32 位)來表示例如select(2)
系統調用所接觸的文件以及如何使用FDSET
宏的標准庫源。
事實是,您可能會遇到問題,即決定是在用於表示位圖的數據類型中跨越 32 位的限制,還是通過使用仍然可用的 64 位類型(暫時)解決該問題。 當您獲得 64 位位掩碼時,這將是下一個規模問題,並且您最終將不得不越界。
作為練習,您現在可以這樣做,您將了解到最后的數據類型或多或少是一組大的位,您可以隨意使用它們。 您是否打算使用 80 位long double
精度值來存儲大於 64 位的位掩碼? 我認為你不會,所以想想陣列解決方案,這可能會一勞永逸地解決你的問題。
如果你的問題是我的情況,我會寫一個 32 位無符號數的數組,所以所有位在移位、位操作等方面的行為都是一樣的。
#define FDSET_TYPE(name, N) unsigned int name[((N) + 31U) >> 5]
#define FDSET_ISSET(name, N) ((name[(N) >> 5] & 1 << (N & 0x1f)) != 0)
...
FDSET_TYPE(name, 126);
...
if (FDSET_ISSET(name, 35)) { ...
在上面的示例中, FDSET_TYPE
宏允許您聲明作為第二個參數傳遞的位數的變量,並使用無符號 32 位整數數組實現它,四舍五入到下一個值以允許包含所有位. FDSET_ISSET(name, 35)
計算單元格和請求位所在的偏移量,並用您傳遞的數字除以 32 的余數對其進行掩碼 --- 但是當我們選擇 2 的冪時,y 使用掩碼0x1f
屏蔽數字的最后 5 位以獲得余數 mod 32)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.