与其他一些语言相比,C的一个有趣特征是它的许多数据类型基于目标体系结构的字长,而不是用绝对术语指定。虽然这允许该语言用于在某些类型可能有困难的机器上编写代码,但是这使得设计将在不同体系结构上一致运行的代码变得非常困难。考虑以下代码:
uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;
在int
16位(许多小型微控制器仍然适用)的体系结构上,此代码将使用定义良好的行为将值分配为1。在int
64位机器上,它将再次使用定义良好的行为分配值4294836225。在int
32位机器上,它可能会分配-131071的值(我不知道这是实现定义的还是未定义的行为)。即使代码只使用名义上称为“固定大小”类型的内容,该标准也要求当今使用的两种不同类型的编译器将产生两种不同的结果,而当今许多流行的编译器将产生三分之一。
这个特定的示例有些人为的,因为我不希望在现实世界的代码中将两个16位值的乘积直接分配给一个64位值,但是它被选择作为一个简短的示例来展示整数的三种方式促销可能与固定大小的无符号类型进行交互。在现实世界中,有必要根据数学整数算术规则对无符号类型的数学进行运算;在其他情况下,有必要根据模数运算的规则进行无符号类型运算;而在某些实际情况下,实际情况并非如此。没关系。诸如校验和之类的许多现实世界代码都依赖于uint32_t
算术包装mod2³²,并且能够执行任意uint16_t
算术和获得的结果至少被定义为准确的mod 65536(与触发未定义行为相反)。
尽管这种情况显然似乎是不希望的(并且随着64位处理成为许多用途的规范,这种情况将变得越来越多),但据我所观察到的C标准委员会更喜欢引入已在某些著名产品中使用的语言功能。环境,而不是“从头开始”发明它们。C语言是否有任何显着扩展,这些扩展允许代码不仅指定如何存储类型,而且还指定在涉及可能晋升的场景中其应如何表现?我至少可以看到编译器扩展解决此类问题的三种方式:
通过添加一条指令,该指令将指示编译器将某些“基本”整数类型强制为一定大小。
通过添加一条指令,该指令将指示编译器评估各种升级方案,就像计算机的类型具有特定的大小一样,而与目标体系结构上类型的实际大小无关。
通过允许声明具有特定特征的类型(例如,声明一个类型应表现为mod-65536环绕的代数环,而不管其底层字长如何,并且不应隐式转换为其他类型;将a加到a
wrap32
上int
应产生a类型的结果(wrap32
无论是否int
大于16位),而将awrap32
直接添加到awrap16
应该是非法的(因为两个都不能转换为另一个)。
我自己的选择是第三个选择,因为它甚至可以使具有异常字长的机器也可以使用很多代码,这些代码希望变量像“二次幂”一样“自动换行”。编译器可能必须添加位屏蔽指令以使该类型具有适当的性能,但是如果代码需要包装mod 65536的类型,则最好让编译器在需要它的机器上生成此类屏蔽,而不是使源代码杂乱无章。或者在需要掩蔽的机器上根本无法使用此类代码。我很好奇,但是,是否有任何通用扩展可以通过上述任何一种方式,或者通过我没有想到的某种方式来实现可移植的行为。
为了澄清我在找什么,有几件事;最为显着地:
尽管可以通过多种方式来编写代码,以确保所需的语义(例如,定义宏以对特定大小的无符号操作数执行数学运算,以产生明确包装或不包装的结果),或至少防止不希望的情况语义(例如,有条件地定义一个类型
wrap32_t
是uint32_t
在编译器,其中一个uint32_t
也不会得到提升,而这一数字是更好地为需要的代码wrap32_t
到一些机器上该类型将得到提升,而不是有它运行并产生虚假行为失败编译)如果有任何一种编写代码的方式对将来的语言扩展最有利,那么使用该方式将比设计自己的方法更好。对于如何扩展语言以解决许多整数大小的问题,我有一些很扎实的想法,允许代码在具有不同字长的机器上产生相同的语义,但是在我花大量时间编写它们之前,我想知道已经朝这个方向做了什么努力。
绝不希望我鄙视C标准委员会或他们所做的工作;但是,我希望在几年后,有必要使代码在“自然”升级类型为32位以及64位的计算机上正确工作。我认为,对语言进行一些适度的扩展(比C99 nnd C14之间的许多其他更改更适度),不仅可以提供一种有效使用64位体系结构的简洁方法,而且在讨价还价中还可以促进与该标准历来向后弯腰以支持“不寻常字长”的机器(例如,使具有12位的机器char
可以运行期望uint32_t
包装mod2³²]。根据未来扩展的方向,我还希望可以定义宏,这些宏将使今天编写的代码可在默认整数类型表现为“预期”的当今编译器上使用,也可在未来整数使用的编译器上使用类型将默认具有不同的行为,但是在哪里可以提供所需的行为。
int
大于uint16_t
,则乘法的操作数将提升为int
,并且乘法将作为int
乘法进行,并且将结果int
转换为int64_t
,以初始化who_knows
。