uint8、uint16等的用法

Usage of uint8, uint16 etc

本文关键字:用法 uint16 uint8      更新时间:2023-10-16

目前我正在使用一个针对32位MIPS平台的代码库(C,C++混合)。处理器是一个相当现代化的处理器[只需提及我们有大量的处理能力和内存]。

代码库使用数据类型,如uint8[1字节宽的无符号整数]、uint16[2字节宽的未符号整数]和uint32[4字节宽无符号整数等。

我知道在将代码移植到不同的平台时,使用这些构造是多么有用。

我的问题是:

  1. 使用uint16有什么用/好处,其中uint32也足够了(如果有的话)?

  2. 使用较短的数据类型(考虑数据对齐)会节省内存使用吗?

  3. 如果是为了节省几个字节的内存,那么在现代硬件中这样做明智吗?

使用uint16有什么用/好处,其中uint32也足够了(如果有的话)?

如果这些uint16s是数组或结构的一部分,那么与相同数组或结构中的uint32s相比,您可以节省内存,也许能够处理更大的数据集。这实际上取决于您的代码。

数据协议和文件格式可能使用uint16s,而使用uint32s可能不正确。这取决于格式和语义(例如,如果您需要从65535到0的值,uint16会自动执行,而uint32不会)。

OTOH,如果这些uint16s只是单个局部或全局变量,那么用32位变量替换它们可能没有显著差异,因为它们可能会由于对齐而占用相同的空间,并且它们无论如何都会作为32位参数(在堆栈或寄存器中)在MIPS上传递。

使用较短的数据类型(考虑数据对齐)会节省内存使用吗?

可能会有节省,特别是当uint16s是大阵列的许多结构或元件的一部分时。

如果是为了节省几个字节的内存,那么在现代硬件中这样做明智吗?

是的,您降低了内存带宽(这总是一件好事),并且当您对较少的数据进行操作时,您经常会降低各种缓存未命中(数据缓存和TLB)。

首先,如果您定义了uint16等类型,它们在哪里定义?它们不是标准类型,因此将在某些专有标头中定义——可能是您的,也可能是由某些第三方库提供的;在这种情况下,您必须问问自己该代码的可移植性如何,以及您是否正在创建一个在其他应用程序中可能没有意义的依赖项。

另一个问题是,许多库(不明智的IMO)用各种名称来定义此类类型,如UINT16、UINT16、U16-UI16等,这在某种程度上成为了确保类型一致性和避免名称冲突的噩梦。如果定义了这样的名称,则理想情况下应将它们放置在命名空间中,或为其指定特定于库的前缀,以指示它们被定义用于哪个库,例如rtos::uint16rtos_uint16

由于ISO C99标准库在stdint.h中提供了标准位长度特定的类型,因此您应该更喜欢使用它们,而不是在专有或第三方标头中定义的任何类型。这些类型具有_t后缀,例如uint16_t。在C++中,它们可以放在std::名称空间中(尽管这不是给定的,因为C99中引入了标头)。

1] 使用uint16有什么用/好处,其中uint32也足够了(如果有的话)?

除了我之前建议更喜欢stdint.huint16_t之外,使用长度特定类型至少有两个合理的理由:

  1. 匹配特定硬件寄存器宽度
  2. 在不同的体系结构中实施通用且兼容的API

2] 使用较短的数据类型(考虑数据对齐)会节省内存使用吗?

可能吧,但如果记忆力不是你的问题,那就不是使用它们的好理由。对于大型数据对象或数组,也许值得考虑,但在全局范围内应用很少值得。

3] 如果是为了节省几个字节的内存,那么在现代硬件中这样做明智吗?

参见[2]。"现代硬件"并不一定意味着拥有大量资源;有很多32位ARM Cortex-M设备,例如只有几Kb的RAM。这更多的是关于模具空间、成本和功耗,而不是关于设计或架构的年龄。

cstdint具有用于不同目的的typedef负载。

  • 特定宽度的intN_t
  • int_fastN_t表示最快的整数,至少有N位
  • 最小整数的int_leastN_t,至少有N位
  • 它们的unsigned等价物

你应该根据自己的情况做出选择。在std::vector中存储数千,而不进行大量计算?intN_t可能是你的人。需要对少量整数进行快速计算吗?int_fastN_t可能就是你的人。

ans。1.软件具有某些要求和规范,这些要求和规范严格告诉在编码/解码或某些其他特定用途时仅取参数的8/16位。因此,即使你给u8分配了一个大于127的值,它也会自动为你修剪数据。

Ans。2.我们不应该忘记,我们的编译器在进行优化方面远远超出了智能范围,无论是内存还是复杂性。因此,建议您尽可能使用较小的内存。

Ans。3.当然,在现代的h/w上保存内存是有意义的。

必须检查生成的机器代码/汇编程序,以验证是否保存了代码。在RISC类型的体系结构中,典型的立即数是16位,但使用uint16_t无论如何都会消耗一个完整的32位寄存器——因此,如果使用int类型,但承诺使用接近零的值将产生相同的结果,并且更具可移植性。

IMO节省内存在现代平台中也是值得的。更严格的代码可以带来更好的电池寿命和更流畅的用户体验。然而,我建议只有在使用(大型)阵列时,或者当变量映射到一些真正的硬件资源时,才对大小进行微观管理。

ps。编译器很聪明,但编写它们的人目前正在努力使它们变得更好。

使用uint16_t而不是uint32_t可以节省内存。它也可能是硬件限制(例如,某些外围控制器确实发送了16位!)然而,由于缓存和对齐方面的考虑,它可能不值得使用(您确实必须进行基准测试)。

问题的答案可以归结为一个关键概念:数据有多大?如果您正在处理大量数据,那么使用较小数据类型的好处是显而易见的。这样想吧:简单地计算新发现的已知最大素数可能会在典型的工作站上耗尽内存。这个数字本身就需要超过1 GB的存储空间。这还不包括计算实际数字。如果要使用较厚的数据类型而不是较薄的数据类型,则可能会使用2GB。这是一个简单的例子,但却是一个好例子。

使用精确宽度的整数类型(如int32_t和friends)有助于避免intlong大小不同的平台之间的符号扩展错误。例如,当应用位掩码或位移位时,可能会发生这种情况。例如,如果您在long上执行这些操作,并且您的代码适用于32位long,那么它可能会中断为64位long。另一方面,如果您使用uint32_t,那么无论平台如何,您都可以确切地知道会得到什么结果。

它们对于在平台之间交换二进制数据也很有用,在平台之间,您只需担心存储数据的字节序,而不必担心位宽;如果您将int64_t写入文件,您就知道在另一个平台上可以读取它并将其存储到int64_t中。如果你在一个平台上写的是64位的long,那么在另一个平台可能最终需要long long,因为long只有32位。

节省内存通常不是原因,除非你谈论的是非常有限的环境(嵌入的东西)或大型数据集(比如一个有5000万元素的数组等等)

使用uint16有什么用/好处,其中uint32也足够了(如果有的话)?

存在unsigned char为16位值的CPU。如果不使用typedefs(uint16只是适当类型的typedef),单元测试这样的代码将很困难。

此外,使用这些typedef,可以更容易地在不同的平台上构建,而不会出现很多问题。

使用较短的数据类型(考虑数据对齐)会节省内存使用吗?

不,这不是重点。如果uint16unsigned short的typedef,那么您可以在任何地方使用unsigned short,但在不同的平台上可能会得到不同的类型。

当然,使用较小的类型会减少内存消耗。例如,使用uint16而不是uint32,但前提是使用数组。

如果是为了节省几个字节的内存,那么在现代硬件中这样做明智吗?

这取决于平台:

  • 更少的内存使用意味着更少的缓存未命中
  • 如果支持,则存在处理16位数据的SIMD函数