[英]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.