16

直到最近,我还认为大多数系统实现者/供应商决定int在 64 位机器上保持纯 32 位是一种权宜之计。对于现代 C99 固定大小类型(int32_tanduint32_t等),对于每个大小为 8、16、32 和 64 的标准整数类型的需求大部分都消失了,似乎int也可以做成 64 位.

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

这是int在实际实现中永久固定在 32 位的一个很好的理由吗?我倾向于说是的。在我看来,可能有一大类用途,当大于 32 位时会uint32_t中断。int即使应用一元减号或按位补码运算符也会变得危险,除非您重新转换为uint32_t.

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

4

8 回答 8

7

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

这将主要隐藏在您只进行算术运算并分配回uint32_t. 但在你与常数进行比较的地方,它可能是致命的。我不知道依赖这种比较而不进行显式转换的代码是否合理。像这样的铸造常量(uint32_t)1会变得非常乏味。我个人至少总是U对我想要无符号的常量使用后缀,但这已经不像我想要的那样可读。

还要记住,uint32_t不能保证 etc 存在。甚至没有uint8_t。对此的执行是 POSIX 的扩展。所以从这个意义上说,C 作为一门语言还远远不能做到这一点。

于 2010-12-30T08:38:34.123 回答
5

“合理的代码”...

嗯...关于开发的事情是,您编写并修复它,然后它可以工作......然后您停止!

也许你已经被烧伤很多,所以你保持在某些功能的安全范围内,也许你没有以那种特殊的方式被烧伤,所以你没有意识到你正在依赖一些可以善良的东西——的变化。

甚至你依赖于一个错误。

在旧的 Mac 68000 编译器上,int 是 16 位,long 是 32。但即便如此,大多数现存的 C 代码都假定 int 是 32,因此您在新闻组中找到的典型代码将无法工作。(哦,Mac 没有 printf,但我离题了。)

所以,我要说的是,是的,如果你改变任何东西,那么有些东西就会坏掉。

于 2010-12-30T01:33:04.320 回答
3

DEC Alpha 和 OSF/1 Unix 是最早的 64 位版本的 Unix 之一,它使用 64 位整数——一种 ILP64 架构(意思是intlong和指针都是 64 位数量)。它引起了很多问题。

我没有看到提到的一个问题——这就是为什么我在这么久之后才回答的原因——如果你有一个 64 位的int,你用什么大小short?16 位(经典的,无变化的方法)和 32 位(激进的“好吧,ashort应该是 anint方法的一半”)都会出现一些问题。

使用 C99<stdint.h><inttypes.h>标头,您可以编码为固定大小的整数 - 如果您选择忽略具有 36 位或 60 位整数(至少是准合法的)的机器。但是,大多数代码不是使用这些类型编写的,并且代码中通常存在根深蒂固且很大程度上隐藏(但存在根本缺陷)的假设,如果模型偏离现有的变体,这些假设将令人不安。

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

于 2010-12-31T22:38:15.150 回答
3

对于现代 C99 固定大小类型(int32_t 和 uint32_t 等),对于每个大小为 8、16、32 和 64 的标准整数类型的需求基本消失了,

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

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

  • int8_t = 字符
  • int16_t = 短
  • int32_t =非标准类型
  • int64_t = int、long 或 long long

来自64 位编程模型:为什么选择 LP64?

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

于 2010-12-30T01:54:53.240 回答
2

如果 int 是 64 位,则有一个代码习惯用法会中断,而且我经常看到它,以至于我认为它可以称为合理的:

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

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

Microsoft 用于测试 HRESULT 的宏同时使用了这两种方法——而且我敢肯定,在不使用 SDK 宏的情况下,有大量代码可以实现类似的功能。如果 MS 已转移到 ILP64,这将是导致移植头痛的一个领域,而 LLP64 模型(或 LP64 模型)可以完全避免这些问题。

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

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

无论如何,我认为这无论如何都不是一个无法解决的问题,但我确实认为这是一种相当普遍的编码实践,如果迁移到 ILP64 平台会导致大量代码需要修复。

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


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

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

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

于 2010-12-31T04:37:59.257 回答
1

虽然我个人不会编写这样的代码,但我敢打赌它存在于不止一个地方......当然,如果你改变int.

int i, x = getInput();
for (i = 0; i < 32; i++)
{
    if (x & (1 << i))
    {
        //Do something
    }
}
于 2010-12-30T02:01:47.863 回答
0

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

于 2010-12-30T01:32:49.217 回答
-1

不是特别。int 在某些 64 位架构(不是 x64)上是 64 位的。

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

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

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

于 2010-12-30T01:43:25.217 回答