在c99中很长很长

Long long in c99

他们在 C99 标准中引入了 long long。这样做的目的是什么?在我(有限的)C 编程经验中,我只见过一个 4 字节的 int 和一个 8 字节的 long。例如,从编译器资源管理器:

如果long已经是8那么,为什么还要添加另一个long long类型呢?这对 compiler/architecture 有什么影响?

C标准只保证一个int可以是(松散的)2个字节,一个long可以是4个字节,一个long long可以是8个字节。

事实上,MSVC 仍然使用 4 字节 long,即使它有 4 字节 int

If long is already 8 then, why is it necessary to add another long long type? What does this do to the compiler/architecture?

“如果 long 已经是 8”并不总是正确的,因为存在许多依赖 32 位 longint 作为 32 或 16 位的代码。

要求 long 作为 64 位会破坏代码库。这是一个主要问题。


然而,要求 long 保持 32 位(而不是 long long)将无法访问标准的 64 位整数,因此 long long 的基本原理。

允许 long 作为 32 位或 64 位(或其他)允许转换。

各种函数像fseek(), ftell()一样传递in/returnlong。他们受益于 long 超过 32 位的大文件支持。

推荐做法鼓励更广泛的 long:“用于 size_tptrdiff_t 的类型不应具有大于 signed long int 除非实现支持足够大的对象以使其成为必要。”这与超过 32 位的内存大小有关。


也许将来的实现可能会使用 int/long/long long/intmax_t 作为 32/64/128/256 位。

IAC,我发现固定宽度类型 intN_tlonglong long 更受欢迎。我倾向于使用固定宽度类型或 bool, (unsigned) char, int/unsigned, size_t, (u)intmax_t 并留下 signed char, (unsigned) short, (unsigned) long, (unsigned) long long 用于特殊情况。

当时和现在 intlong 的唯一相关要求是 int 必须至少为 16 位并且 long 必须至少为 32位。 16 位和 32 位系统都趋向于拥有 32 位 long,而 64 位机器在 1990 年代后期不太常见。因此,在 C99 之前,程序员根本无法移植地依赖 64 位整数类型。这个问题通过引入 long long 来解决,它至少需要 64 位。 (我相信它已经由 GCC 和其他编译器作为扩展提供)。

这些天,很多(但不是全部)64位系统使用64位long并且不费心去制作long long任何更大,所以它也是 64 位的,并且在某种意义上是多余的。这些大概是您所熟悉的系统,但它们并不代表那里的一切。

我认为您没有意识到您对 C 类型宽度要求的工作方式做出了一个巨大的错误假设:ISO C 只是设置了一个最小值范围,例如允许的最小值 LONG_MAXLONG_MIN(-2147483647,不是 8,因为 ISO C 允许一个补码和 sign/magnitude 有符号整数,而不仅仅是 2 的补码。)实际实现允许具有更宽的类型,通常是为了匹配目标机器可以有效执行的寄存器宽度或操作数大小。

在 Stack Overflow 和其他地方已经写了很多关于这个的文章,我不打算在这里重复。另见 https://en.cppreference.com/w/c/language/arithmetic_types


这导致您错误地查看 x86-64 System V ABI 中的类型宽度选择并假设其他 C 实现是相同的,我认为。 x86-64 是一个 64 位 ISA,可以高效地处理 64 位整数,因此 64 位 long 是一个相当明智的选择。

对于像 i386 这样的 32 位机器,没有理智的 ABI 会使用 64 位 long 因为这不是必需的,只有 32 位。使用 64 位意味着它无法放入单个寄存器中。 -m32 编译,或为 32 位 ARM 编译。 Godbolt 也有用于 AVR 和 MSP430 的 GCC。在那些 8 位和 16 位机器上,GCC 选择 ISO C 允许的最小宽度(2 字节 int,等等)

1999 年,x86-64 甚至还不存在。 (其他一些 64 位 ISA 也这样做了,比如 Alpha)。因此,查看 2 个主流 ABI 之一以了解 C99 选择不会让你走得太远。

当然,C 需要保证至少是 64 位的类型,让人们编写能够高效地进行 64 位整数数学运算的程序。


顺便说一句,x86-64 可以像 64 位一样高效地处理 32 位整数,有时效率更高。因此,使 long 成为 64 位类型可以说不是很好。一些代码使用 long 是因为他们想要一个需要 32 位的类型,但不能从更宽的类型中获益。对于这样的代码,64 位 long 只会浪费缓存占用空间/内存带宽和代码大小(REX 前缀)。在 C99 中,理想的选择是 int_least32_t,但输入起来很烦人而且很少使用。

但是 OTOH,long 有时希望成为“最有效的(1 寄存器)类型”,尽管没有这样的保证和 LLP64 ABI,例如 Windows x64 和 32 位 long 不是那样的。

C99 int_fast32_t 和 x86-64 系统 V 的 IMO 糟糕选择使它成为 64 位类型。 (我对 Cpp uint32_fast_t resolves to uint64_t but is slower for nearly all operations than a uint32_t (x86_64). Why does it resolve to uint64_t? 有一个半写的答案,我应该完成...... int_fast32_t 提出了“为了什么目的而快速”的问题,并且在许多实现中,这并不是你希望的那样案例。

另见

  • C++ - the fastest integer type?
  • How should the [u]int_fastN_t types be defined for x86_64, with or without the x32 ABI?
  • Why is uint_least16_t faster than uint_fast16_t for multiplication in x86_64?
  • Compiler optimizations allowed via "int", "least" and "fast" non-fixed width types C/C++

有一些限制,但编译器作者可以自由选择标准 C 变量类型(char、short、int、long、long long)的长度。对于该架构,char 自然是一个字节(大多数 C 编译器是 8 位)。当然,你不能让更小的东西大于更大的东西,long 不能小于 int。但可以肯定的是,到 1999 年,我们看到了 x86 从 16 位到 32 位的转变,例如 int 从 16 位变成了 32 位,但长期以来一直是 32 位。后来发生了 32 位到 64 位的 x86 转变,并且根据工具的不同,有可用的类型来帮忙。

这个问题很久以前就存在了,解决方案不是固定类型的长度,它们在规则范围内,由编译器作者决定大小。但是编译器作者需要制作一个匹配工具和目标的 stdint.h 文件(stdint.h 至少特定于工具和目标,并且可以是工具的版本和该工具的构建选项等).因此,例如,uint32_t 始终是 32 位。一些作者会在他们的 stdint.h 中将其转换为 int 其他人 long 等。 C 语言变量类型仍然限于 char、short、int 等(uint32_t 不是变量类型,它通过 stdint.h 转换为变量类型)。 solution/workaround 是一种让 is 免于发疯并保持语言活力的方法。

作者经常会选择,比如GPRs是16位就int是16位,32位就是32位等等,但是他们有一定的自由度。

是的,这具体意味着没有理由假设针对特定目标(例如您正在阅读本文的计算机)的任何两个工具对 int 和 long 使用相同的定义,特别是如果您想为这个平台编写可以跨这些工具(支持这个平台)的代码,然后使用 stdint.h 类型而不是 int、long 等......如果你跨平台 msp430 mcu,最肯定的是arm mcu,一个 arm linux 机器,一个基于 x86 的机器,即使对于相同的“工具链”(例如 gnu gcc 和 binutils),类型对 int 和 long 等的定义也不相同。 char 和 short 往往是 8 位和 16 位,int 和 long 往往变化最大,有时大小相同有时不同,但重点是不要假设。

对于编译器 version/target/command 行选项,检测大小是微不足道的,或者只是走 stdint 路线以尽量减少以后的问题。