"int"和"int_fast16_t"有什么区别?

44
据我了解,C规范规定类型int应该是目标平台上至少包含16位的最高效类型。
那C99对于int_fast16_t的定义难道不也是这样吗?
也许他们之所以把它放在那里只是为了保持一致性,因为其他的int_fastXX_t也是必需的吗?
更新:
总结以下下面的讨论:
我的问题在很多方面都是错误的。C标准没有为指定比特数。它给出了一个范围[-32767,32767],它必须包含。
起初我意识到,大多数人会说,“但那个范围意味着至少16位!”但是C并不要求用二进制补码存储整数。如果他们说“16位”,可能有些平台有1位奇偶校验位、1位符号位和14位大小,仍然“符合标准”,但不能满足那个范围。
标准没有说明是最有效的类型。除了上述大小要求外,可以由编译器开发人员根据其认为最重要的任何标准来决定。 (速度、大小、向后兼容性等)
另一方面,就像向编译器提供提示,告诉它应该使用最优性能的类型,可能会牺牲其他任何权衡。
同样,会告诉编译器使用>=16位的最小类型,即使速度较慢也是如此。对于保留大型数组等空间非常有用。

例子: 尽管在64位系统上MSVC使用x86-64有32位的int,但MicroSoft选择这样做是因为太多人认为int总是恰好32位,因此许多ABI会中断。然而,如果64位值在x86-64上更快,那么int_fast32_t可能是一个64位数。(我不认为事实确实如此,但它只是证明了这一点)


1
这不是一个特定于C的问题吗?为什么要加上C++标签? - Olivier Poulin
7
C++ 从 ANSI-C 继承了类型 "int",而新的 C++11 标准继承了所有 C99 <inttypes.h> 中定义的类型。我认为这个问题同样适用于 C++。 - something_clever
@ask_me_about_loom:但是你已经知道为什么C++包含它们了:因为它们是C的一整个部分,而C++则整体包含了C。所以你实际上的问题只是关于C的。 (顺便说一句,这是一个有趣的问题!) - ruakh
@Lundin 我建议你阅读此线程的其余部分,因为你的陈述比下面给出的其他答案不够准确。具体来说,C标准没有说明'int'是否>= 15位。 - something_clever
@Lundin 你正在争论我最初提出的完全相同的观点,直到在其他线程中Kevin向我展示了这种细微差别。你是对的,我永远无法制作出少于15位+符号的持有该范围的类型,但我可以制作一个16位但不持有该范围的类型。(例如-奇偶校验位)。这就是为什么标准规定范围而不是说“16位”的原因。 - something_clever
显示剩余4条评论
7个回答

35

int是速度和大小上最高效的类型,但这并没有在C规范中指定。它必须是16位或更多。

int_fast16_t是速度最快的类型,其范围至少为16位整数。

例如:某个平台可能已经决定由于许多原因,而将int设为32位。同一系统可能会发现另一种类型是最适合16位整数的。

例如:在64位机器上,人们预期int应该是64位,但编译器为了兼容性,在32位int编译模式下运行。在此模式下,int_fast16_t可以是64位,因为这是本地最快的宽度,避免了对齐问题等。


7
这似乎是最有意义的解释... 所以你是说,“int”可能不是为了速度而被选中,而是编译器开发人员为平台做出的某种空间/时间权衡,而int_fast16_t就像是你在说:“我不在乎空间,我想要速度快”? 看起来是正确的答案... - something_clever
1
C标准中没有包含“最高效类型”这个短语,也没有明确说明类型int(或者说是对类型int的操作)的效率问题。 - Keith Thompson
1
@Keith Thompson C语言在速度和尺寸方面都没有太多的效率保证。我从OP的帖子中得出“最高效类型”,并试图将“效率”的概念从单一维度问题(速度)转向包括速度、尺寸、兼容性等多个问题,其中int试图满足。 int_fast16_t根据规范旨在成为至少具有16位的快速数据类型之一。 - chux - Reinstate Monica
1
@chux 的确,我想我对这个问题的思考有点过于单一了。他们创建这些“快速”类型来表达“无视其他权衡,我要速度”的意思是有道理的。 - something_clever
1
@chux 同意。C确实是那种“学习一周,精通一生”的语言之一。它的类型系统的微妙之处令人惊叹,哈哈。 - something_clever
显示剩余3条评论

28

int_fast16_t 确保是大小至少为16位的最快速的整型。 int 没有其大小的保证,除了:

 sizeof(char) = 1 and sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long).

它可以容纳-32767到+32767的范围。

(7.20.1.3p2) "typedef名称int_fastN_t指定宽度至少为N的最快带符号整数类型。 typedef名称uint_fastN_t 指定具有宽度至少为N的最快无符号整数类型。"


6
你有关于各种数据类型相对大小的陈述是正确的,但你错误地认为它们没有最小值。C标准规定,“char”至少需要8位,“short”至少需要16位,“int”至少需要16位,“long”至少需要32位。 - something_clever
4
“int”数据类型确实需要至少16位。这是C标准规定的。 - ElderBug
4
通过指定一个范围,他们必然要指定一个最小值大小。仅仅因为他们没有说这些话并不意味着暗示不存在。在基于2进制的数制中,用少于16个位无法表示[-32767,32767],且C语言不适用于非基于2进制的数制。 - something_clever
7
如果标准规定“最少16位”,那么即使不符合范围要求,一个16位符号尾数实现也是合法的。这就是范围被指定的原因,也是范围是描述标准实际含义的唯一正确方式的原因。 - Kevin
6
@Kevin 天哪,你说得对!我想可能有一种类型可以有16位,但仍然不能容纳那个范围!哇!我的思维被震撼了!我一直想知道为什么他们给出范围,而不只是说“16位”。这是一个非常好的观点! - something_clever
显示剩余11条评论

8
据我了解,C规范指出类型int应是目标平台上至少包含16位的最有效类型。
下面是有关int的标准实际内容:(N1570 draft,第6.2.5节,第5段):
“普通”int对象具有执行环境体系结构建议的自然大小(足以包含头文件<limits.h>中定义的范围内任何值的大小INT_MININT_MAX)。
INT_MININT_MAX的引用可能略微误导人;这些值是基于类型int的特征选择的,并非反过来。
而短语“自然大小”也略微误导。根据目标体系结构,整数类型可能不止一个“自然”大小。
在其他地方,标准规定INT_MIN必须至多为-32767INT_MAX必须至少为+32767,这意味着int至少为16位。
以下是标准对int_fast16_t的规定(7.20.1.3):
以下每种类型都指定了一种整数类型,通常在具有至少指定宽度的所有整数类型中,它是操作速度最快的。
附注如下:
指定的类型不能保证在所有情况下都是最快的;如果实现没有选择一个类型而是选择满足符号和宽度要求的某些整数类型,则表示实现没有明确的选择理由。 intint_fast16_t的要求类似但并不相同,它们同样含糊不清。
实际上,选择int的大小通常是基于除了“自然大小”之外的标准,或者为方便起见解释这个短语。通常,为了最小化移植代码的难度,新架构的int大小被选择为与现有架构的大小相匹配。并且有一个相当强的动机使int不超过32位,以便类型charshortint可以覆盖8、16和32位的大小。在64位系统上,特别是x86-64上,“自然”的大小可能是64位,但大多数C编译器将int设置为32位而不是64位(甚至有些编译器将long设置为32位)。
我怀疑选择int_fast16_t的底层类型与此类考虑无关,因为使用它的任何代码都明确要求快速的16位有符号整数类型。许多现有的代码对int的特性做出了超出标准保证的假设,如果编译器开发人员希望使用他们的编译器,就必须迎合这样的代码。

1
感谢您的第一个点赞。您真的有时间在24秒内阅读完所有内容吗? - Keith Thompson
太棒了的解释:C标准的摘录和实际例子紧随其后……我真的无法要求更好了。谢谢! - something_clever
@ask_me_about_loom; 这里还有其他表达感谢的方式。 - haccks

2
区别在于带有 "快速" 前缀的类型允许比其对应的类型(不带 "快速" 前缀)更宽,以提高效率/优化目的。但是,C 标准绝不保证它们实际上更快。
C11,7.20.1.3 最快的最小宽度整数类型
每个以下类型指定一个整数类型,通常在所有具有至少指定宽度的整数类型中,它们是最快操作的。
typedef名称 int_fastN_t 指定至少为N的最快签名整数类型。typedef名称 uint_fastN_t 指定至少为N的最快无符号整数类型。
指定类型不能保证在所有情况下都最快;如果实现没有明确选择一种类型而非另一种类型的理由,则只需选择满足有符号性和宽度要求的某些整数类型即可。
另一个区别是 “快速” 和 “最小宽度” 类型是必需类型,而其他确切宽度类型是可选的。例如:int_fast8_t、int_fast16_t、int_fast32_t、int_fast64_t、uint_fast8_t、uint_fast16_t、uint_fast32_t 和 uint_fast64_t。

这绝不意味着它们一定更快。但它们不能更慢。 - Persixty
@DanAllen 标准只是提供了选择这些底层类型的灵活性。我不认为它规定了它不应该更慢。当然,编译器不会故意这样做。如果实现为 int_fast8_t 选择某种类型,结果比 int8_t 更慢,您是否建议它违反了标准的任何要求? - P.P
我认为,在描述它们为“最快”的时候,我们应该把它们视作最快的。它并没有定义最快是什么。然而,经过反思,我撤回了“必须”这个词,并且使用“应该”。规范允许某些操作更快,而其他操作更慢,但在实践中,架构往往不是这样工作的。 - Persixty

2
从C99解释文档“7.8 整数类型转换格式< inttypes.h>”中(该文档是标准的附属文件),可以看出C89规定了语言应支持四种带符号和无符号整数数据类型,即char、short、int和long,但没有对它们的大小做出太多要求,除了int和short至少为16位,long长度不小于int且不小于32位。对于16位系统,大多数实现分别将8、16、16和32位分配给char、short、int和long。对于32位系统,通常将这些类型分配为8、16、32和32位。由于int大小的差异,当用户从一个分配不同整数类型大小的系统迁移到另一个系统时,标准C的整数提升规则可能会产生意想不到的静默更改,因此需要定义扩展整数类型,随着64位系统的引入,对扩展整数的需求增加了。 的目的是提供一组整数类型,其定义在机器之间保持一致并独立于操作系统和其他实现特性。它通过typedef定义各种大小的整数类型。实现可以将它们typedef为标准C整数类型或它们支持的扩展。一致使用此头文件将极大地增加用户程序跨平台的可移植性。
int和int_fast16_t之间的主要区别在于后者可能不会受到这些“实现特性”的影响。可以将其视为:“我不关心当前OS /实现‘政治’中int大小的问题。只要给我至少16位的最快速度的有符号整数类型。”

1
在一些平台上,使用16位值可能比使用32位值慢得多(例如,8位或16位存储需要执行32位加载、修改已加载的值并写回结果)。即使可以将两倍于32位值的16位值适配到高速缓存中(在32位系统中,16位值会比32位值更快),但每次写操作都必须在读操作之前进行,这将抵消任何可能产生的速度优势,除非读取数据结构的频率远高于写入。在此类平台上,像int_fast16_t之类的类型可能是32位。
话虽如此,不幸的是,标准没有允许编译器采用最有用的语义,即允许未获取地址的int_fast16_t类型变量根据方便随意表现为16位类型或更大类型。例如,考虑以下方法:
int32_t blah(int32_t x)
{
  int_fast16_t y = x;
  return y;
}

在许多平台上,存储在内存中的16位整数通常可以像存储在寄存器中的那样进行操作,但是没有指令可在寄存器上执行16位操作。如果存储在内存中的int_fast16_t变量只能容纳-32768到+32767,则同样的限制也适用于存储在寄存器中的int_fast16_t变量。由于将超大值强制转换为过小以容纳它们的有符号整数类型是实现定义的行为,这将迫使上述代码添加指令以对x的低16位进行符号扩展,然后返回它;如果标准允许这样的类型,则具有灵活性的“至少16位,但更方便”的类型可以消除对这些指令的需求。

1
两种类型可能不同的例子:假设有一种体系结构,其中8位、16位、32位和64位算术同样快(i386接近)。那么,实现者可以使用LLP64模型,或更好地允许程序员在ILP64、LP64和LLP64之间进行选择,因为有很多代码假定 long 精确地是32位,并且 sizeof(int) <= sizeof(void*) <= sizeof(long)。任何64位实现都必须违反至少其中一个假设。
在这种情况下,int 可能会是32位宽,因为这将破坏其他系统中最少的代码,但 uint_fast16_t仍然可以是16位宽,节省空间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接