uint8_t,uint_fast8_t和uint_least8_t之间的区别


75

C99标准引入了以下数据类型。可以在此处找到有关AVR stdint库的文档。

  • uint8_t 表示它是8位无符号类型。
  • uint_fast8_t 表示它是最快的无符号整数,至少8位。
  • uint_least8_t 表示它是一个至少8位的无符号整数。

我了解uint8_t什么是uint_fast8_t(我不知道它如何在寄存器级别实现)。

1.您能解释一下“它unsigned int至少有8位”的含义吗?

2.uint_fast8_tuint_least8_t相比如何以及uint8_t如何帮助提高效率/代码空间?


对于您的第一个问题,我可以想象,尽管uint8_t保证为8位,但uint_fast8_t保证为> = 8位,非常像unsigned char
雅各布

1
一个考虑因素是在uint8_t没有本机8位类型的系统上不存在。另外两个会在那里。
皮特·贝克尔

9
您已经获得了涉及“晦涩”和“异国”架构的答案。这些术语有些偏颇。当然,如果您唯一的经验是台式机系统,那么这些体系结构将超出您的经验范围。但是“我以前从未见过”与“这是晦涩或异国情调”不同。对于使用嵌入式系统或DSP的人来说,这些事情很普遍。
皮特·贝克尔

Answers:


99

uint_least8_t是具有至少8位的最小类型。 uint_fast8_t是具有至少8位的最快类型。

您可以通过想象异国情调的体系结构来看到差异。想象一下20位架构。它unsigned int有20位(一个寄存器),unsigned char还有10位。因此sizeof(int) == 2,但使用char类型需要额外的指令才能将寄存器切成两半。然后:

  • uint8_t:未定义(无8位类型)。
  • uint_least8_t:是unsigned char,最小类型为至少8位。
  • uint_fast8_t:is unsigned int,因为在我的虚构体系结构中,半寄存器变量比全寄存器变量慢。

11
我喜欢您必须如何想象异国情调的体系结构才能找到用例。他们在实践中发现有用吗?
user541686 '16

18
@Mehrdad例如,在ARM中,如果您int_fast8_t是32位变量,则无需在算术运算之前进行符号扩展。
user694733 '16

1
以@Mehrdad MIPS为例,使任何uintX_fast_t少于32位都是非常错误的。您甚至不必想象体系结构变得uint8_t不确定,例如36位的UNIVAC,我假设有char9位。
上空

7
@Mehrdad:我承认我从未见过uint_leastX_tuint_fastX_t在实际应用中使用过。uintX_t是的,它们被大量使用。看起来人们对于异国架构的可移植性不是很感兴趣。这是可以预期的,即使您正确设置了未签名的位置,您的程序也会在千种不同的情况下失败。
rodrigo

3
@skyking:我并不是说不应该使用它们,只是在实践中不经常使用它们。如果您可以找到一个合理使用它们的真实应用程序或库,请发布一个链接,因为我找不到任何链接。
rodrigo

29

uint8_t 意思是:给我一个正好8位的无符号整数。

uint_least8_t意思是:给我最小的无符号整数类型,它至少有8位。针对内存消耗进行优化。

uint_fast8_t意思是:给我一个至少8位的无符号整数。考虑到对齐方式,请选择更大的类型,如果它会使我的程序运行更快。优化速度。

另外,与普通int类型不同,上述stdint.h类型的带符号版本保证为2的补码格式。


1
谢谢。很高兴知道in的带符号类型stdint.h必定是二进制补码。想知道在编写可移植代码时它将在哪里提供帮助。
legends2k

6
请注意,只有精确的宽度变体才需要使用2的补码格式。另请注意,这些不是必需存在的。因此,不需要平台来支持2的补码格式。
2016年

@ legends2k:stdint.h如果要尝试编写可移植的代码,则in的类型没有那么有用,因为尽管要求它们使用二进制补码存储格式,但这并不意味着它们将表现出二进制补码包装行为。还要注意,即使在int32位的平台上,也不能保证使用an写入值int32_t*和使用an读取值int*,反之亦然。
超级猫

@supercat我见过的每个编译器都为stdint.h类型使用内部typedef,以使其与一种基本的“关键字”整数类型同义。因此,如果您担心指针别名,我认为在实践中仅在理论上这不会成为问题。
伦丁

@Lundin:某些编译器使用“ long”作为int32_t的typedef,而有些使用“ int”。即使“ int”和“ long”具有相同的表示形式,出于C的别名规则的目的,它们也可能(有时被认为)是不同的。
超级猫

26

该理论类似于:

uint8_t必须精确为8位,但不需要存在。因此,您应该在依赖8位整数的256模分配行为*的地方使用它,并且在编译失败而不是晦涩的体系结构上表现不佳的情况下,应该使用它。

uint_least8_t必须是最小的可用无符号整数类型,可以存储至少8位。当您想最大程度地减少诸如大型数组之类的内存使用时,可以使用它。

uint_fast8_t应该是可以存储至少8位的“最快”无符号类型;但是,对于任何给定处理器上的任何给定操作,实际上并不能保证最快。您将在处理对值执行大量操作的代码中使用它。

实践是,“快速”和“最小”类型使用不多。

“最小”类型仅在您关心可移植性以使CHAR_BIT!= 8使大多数人不了解的体系结构模糊时才真正有用。

“快速”类型的问题在于很难确定“最快”类型。较小的类型可能意味着较少的内存/缓存系统负载,但是使用小于本机的类型可能需要额外的指令。此外,最好在体系结构版本之间进行更改,但是实现者通常希望避免在这种情况下破坏ABI。

通过查看一些流行的实现,似乎uint_fastn_t的定义是相当任意的。glibc似乎将它们定义为至少是有问题的系统的“本机字长”,而没有考虑到许多现代处理器(尤其是64位处理器)对小于本机字的项目进行快速操作的特定支持这一事实。尺寸。IOS显然将它们定义为与固定大小类型等效。其他平台可能会有所不同。

总而言之,如果有微小的整数紧凑的代码性能是你的目标,你应该是基准标记对您关心与不同尺寸的类型,看看有什么效果最好的平台代码。

*请注意,由于C的整数提升功能不完善,遗憾的是256模分配行为并不总是暗示256模算术。


2
glibc的定义是在不存在这些优化的时候选择的,现在它们被烘焙到了ABI中,无法更改。这是_least和_fast类型实际上在实际中没有用的几个原因之一。
zwol

4
@zwol:我希望语言会添加根据布局和语义要求定义的类型类型,例如“我需要低位的东西会别名其他16位类型的东西,并且可以容纳0-65535的值,但是我不愿意不需要将较大的值固定在该范围内”。混叠,布局,范围和超出范围的行为应该是类型的四个不同方面,但是C仅允许某些组合,这些组合在不同平台之间不一致。
超级猫

5

某些处理器在较小的数据类型上无法像在大型数据类型上那样高效地运行。例如,给定:

uint32_t foo(uint32_t x, uint8_t y)
{
  x+=y;
  y+=2;
  x+=y;
  y+=4;
  x+=y;
  y+=6;
  x+=y;
  return x;
}

如果yuint32_t为ARM Cortex-M3的编译器可以简单地生成

add r0,r0,r1,asl #2   ; x+=(y<<2)
add r0,r0,#12         ; x+=12
bx  lr                ; return x

但既然yuint8_t,编译器将不得不生成:

add r0,r0,r1          ; x+=y
add r1,r1,#2          ; Compute y+2
and r1,r1,#255        ; y=(y+2) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#4          ; Compute y+4
and r1,r1,#255        ; y=(y+4) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#6          ; Compute y+6
and r1,r1,#255        ; y=(y+6) & 255
add r0,r0,r1          ; x+=y
bx  lr                ; return x

“快速”类型的预期目的是允许编译器替换无法使用快速类型进行有效处理的较小类型。不幸的是,“快速”类型的语义说明得很差,从而又产生了一个模糊的问题,即将使用带符号数学还是无符号数学来评估表达式。


当处理较小的数据类型与较大的本机字大小的数据类型时,多余的可能不必要的指令将大大说明为什么许多“快速”数据类型的位宽可能比预期的大。谢谢您的榜样。
星系

@Galaxy:不幸的是,该标准不允许使用“最少”类型的行为,这些行为的行为可能会因上下文而异。例如,在许多机器上,对寄存器中的32位值进行算术运算可能比使用寄存器中的8位值进行运算更快,但是8位加载和存储的速度与32位加载和存储以及缓存的速度相同问题可能会导致8位值更有效。
超级猫

4

1.您能解释一下“它是一个至少有8位的无符号整数”的含义吗?

这应该是显而易见的。这意味着它是一个无符号整数类型,并且其宽度至少为8位。实际上,这意味着它至少可以容纳数字0到255,并且绝对不能容纳负数,但是它可以容纳大于255的数字。

显然,如果计划存储0到255范围之外的任何数字(并且希望可移植),则不应使用这些类型中的任何一种。

2.与uint8_t相比,uint_fast8_t和uint_least8_t如何帮助提高效率/代码空间?

uint_fast8_t需要更快,因此如果您的要求是代码要快,则应该使用它。uint_least8_t另一方面,要求没有较小的候选对象-因此,如果要考虑大小,则可以使用该候选对象。


当然,仅uint8_t当您绝对要求它为8位时才使用。使用uint8_t可能会使代码不可移植,这uint8_t是不需要的(因为某些平台上不存在这种小的整数类型)。


3

“快速”整数类型被定义为可用的最快整数,且至少具有所需的位数(在您的情况下为8)。

平台可以定义uint_fast8_tuint8_t速度绝对没有差异。

原因是有些平台在不使用其本机字长时速度较慢。


0

我正在将快速数据类型(uint_fast8_t)用于本地vars和函数参数,并在经常使用的数组和结构中使用普通的数据类型(uint8_t),而内存占用量比不使用即可节省的几个周期更为重要清除或签名扩展高位。除MISRA检查程序外,效果很好。他们从快速类型中发疯。诀窍在于,快速类型是通过派生类型使用的,派生类型可以针对MISRA构建和普通构建进行不同定义。

我认为这些类型非常适合创建可移植的代码,这对低端微控制器和大型应用处理器均有效。改进可能不是很大,或者对于好的编译器来说可以忽略不计,但是总比没有好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.