make plain int 64 bit会破坏很多合理的代码吗?

直到最近,我还是考虑过大多数系统实现者/供应商的决定,即使在64位机器上也能保持32位,这是一种权宜之计。 对于现代C99固定大小类型( int32_tuint32_t等),需要有一个标准整数类型,每个大小8,16,32和64大多消失,似乎int也可以做64位。

但是,C中plain int大小的最大实际结果来自于C本质上没有小于int类型的算术。 特别是,如果int大于32位,则对uint32_t值的任何算术的结果都具有signed int类型,这相当令人不安。

这是否是在实际实现int永久固定为32位的一个很好的理由? 我倾向于说是的。 在我看来,当int大于32位时, uint32_t可能会有大量使用。 除非你回到uint32_t否则即使应用一元减号或按位补码运算符也会变得危险。

当然,同样的问题适用于当前实现的uint16_tuint8_t ,但是每个人似乎都知道并习惯将它们视为“小于int ”类型。

正如你所说,我认为促销规则确实是杀手锏。 uint32_t然后会提升为int ,突然之间你已经签署了算术,几乎每个人都希望无符号。

这将主要隐藏在您只进行算术并分配回uint32_t 。 但是在与常数进行比较的地方可能会致命。 我不知道,在没有进行显式演员的情况下依赖于这种比较的代码是否合理。 像(uint32_t)1这样的(uint32_t)1常量变得非常繁琐。 我个人至少总是使用后缀U作为我想要无符号的常量,但这已经不像我想的那样可读。

还要记住, uint32_t等不能保证存在。 甚至不是uint8_t 。 执行它是POSIX的扩展。 因此,在这种意义上,作为一种语言的C远远不能实现这一目标。

“合理的代码”……

嗯……关于开发的事情是,你编写并修复它然后它起作用……然后你就停止了!

也许你已经被烧了很多,所以你在某些特征的安全范围内保持良好状态,也许你没有以这种特殊的方式被烧伤,所以你没有意识到你依赖的东西可能是 -变化

或者甚至你依赖于一个bug。

在旧版的Mac 68000编译器中,int为16位,长度为32.但即便如此,大多数现存的C代码假设int为32,因此您在新闻组中找到的典型代码将无效。 (哦,Mac没有printf,但我离题了。)

所以,我得到的是,是的,如果你改变了什么 ,那么有些事情会破裂。

对于现代C99固定大小类型(int32_t和uint32_t等),需要有一个标准整数类型,每个大小8,16,32和64大多消失,

C99具有固定大小的类型DEF,而不是固定大小的类型。 本机C整数类型仍然是charshortintlonglong long 。 它们仍然相关。

ILP64的问题在于它与C类型和C99 typedef之间存在很大的不匹配。

  • int8_t = char
  • int16_t =短
  • int32_t = 非标准类型
  • int64_t = int,long或long long

从64位编程模型:为何选择LP64? :

遗憾的是,ILP64模型不提供描述32位数据类型的自然方式,并且必须求助于非可移植构造(如__int32来描述此类型。 这可能会导致产生代码的实际问题,这些代码可以在没有#ifdef结构的情况下在32位和64位平台上运行。 可以将大量代码移植到LP64模型而无需进行此类更改,同时保持对数据集的投资,即使在应用程序未在外部看到键入信息的情况下也是如此。

DEC Alpha和OSF / 1 Unix是Unix的第一个64位版本之一,它使用64位整数 – 一个ILP64架构(意思是intlong和指针都是64位数量)。 它造成了很多问题。

我没见过的一个问题 – 这就是为什么我会在这么久之后回答的问题 – 如果你有一个64位的int ,那么你用什么尺寸来short ? 16位(经典的,无变化的方法)和32位(激进的’井, short应该是int ‘方法的一半)都会出现一些问题。

使用C99 头文件,您可以编码为固定大小的整数 – 如果您选择忽略具有36位或60位整数的机器(至少是准合法的)。 但是,大多数代码不是使用这些类型编写的,并且代码中通常存在深层次且基本上隐藏(但基本上有缺陷的)假设,如果模型偏离现有变体,则会令人不安。

请注意Microsoft针对64位Windows的超保守LLP64模型。 选择它是因为如果更改了32位模型,过多的旧代码会破坏。 但是,由于存在差异,已移植到ILP64或LP64体系结构的代码无法立即移植到LLP64。 阴谋理论家可能会说它是故意选择使64位Unix编写的代码更难以移植到64位Windows。 在实践中,我怀疑这不仅仅是一个快乐(对微软)的副作用; 为了利用LP64模型,必须对32位Windows代码进行大量修改。

如果整数是64位,那么有一个代码习惯用法就会破坏,我认为它经常足以让我认为它可以称为合理的:

  • 通过测试if ((val & 0x80000000) != 0)检查值是否为负数((val & 0x80000000) != 0)

这在检查错误代码中很常见。 许多错误代码标准(如Window的HRESULT )使用位31来表示错误。 并且代码有时会通过测试第31位或有时通过检查错误是否为负数来检查该错误。

微软用于测试HRESULT的宏使用这两种方法 – 我确信在没有使用SDK宏的情况下,有大量的代码可以实现类似的function。 如果MS已经转移到ILP64,那么这将导致移植头痛,这是LLP64模型(或LP64模型)完全避免的。

注意:如果您不熟悉“ILP64”等术语,请参阅答案末尾的迷你词汇表。

我很确定有很多代码(不一定是面向Windows的)使用plain-old-int来保存错误代码,假设这些整数是32位。 我敢打赌,有很多代码都有这种错误状态方案,它也使用两种检查( < 0和第31位被设置),如果移动到ILP64平台则会中断。 如果错误地构造了错误代码以便进行符号扩展,则可以使这些检查继续正常工作,但同样,我见过的许多这样的系统通过将一堆位域组合在一起来构造错误值。 。

无论如何,我不认为这是一个无法解决的问题,但我认为这是一种相当常见的编码实践,如果转移到ILP64平台,会导致很多代码需要修复。

请注意,我也认为这不是微软选择LLP64模型的最重要原因之一(我认为这个决定很大程度上是由32位和64位进程之间的二进制数据兼容性驱动的,如MSDN和Raymond Chen的博客 )。


64位平台编程模型术语的迷你词汇表

  • ILP64: intlong ,指针是64位
  • LP64: long和指针是64位, int是32位(许多(大多数?)Unix平台使用)
  • LLP64: long long和指针是64位, intlong保持32位(在Win64上使用)

有关64位编程模型的更多信息,请参阅“64位编程模型:为什么选择LP64?”

好吧,这个故事不是全新的。 对于“大多数计算机”,我认为你的意思是台式电脑。 已经有从16位到32位int 。 有没有什么说这次不会发生同样的进展?

虽然我不亲自编写这样的代码,但我敢打赌它不止一个地方……当然,如果改变int的大小,它会破坏。

 int i, x = getInput(); for (i = 0; i < 32; i++) { if (x & (1 << i)) { //Do something } } 

不是特别。 在某些64位体系结构(不是x64)上,int是64位。

该标准实际上并不能保证你得到32位整数,只是(u)int32_t可以容纳一个整数。

现在,如果您依赖于int与ptrdiff_t的大小相同,则可能会被破坏。

请记住,C不保证机器甚至是二进制机器。