谁决定任何数据类型或结构的大小(取决于 32 位或 64 位)?

Who decides the sizeof any datatype or structure (depending on 32 bit or 64 bit)?

谁决定任何数据类型或结构的大小(取决于 32 位或 64 位)?编译器还是处理器?例如,sizeof(int) 对于 32 位系统是 4 个字节,而对于 64 位系统是 8 个字节。

我还读到,使用 32 位和 64 位 编译器 编译时,sizeof(int) 是 4 个字节。

假设我的 CPU 可以 运行 32 位和 64 位应用程序,谁将在决定数据大小方面发挥主要作用 编译器或处理器?

归根结底是编译器。编译器实现者可以决定模拟他们认为合适的任何整数大小,而不管 CPU 最有效地处理什么。也就是说,C(和 C++)标准是这样写的,编译器实现者可以自由选择最快和最有效的方式。对于许多编译器,实现者选择将 int 保留为 32 位,尽管 CPU 本机非常有效地处理 64 位 int。

我认为这样做的部分原因是为了提高程序的可移植性,当时 32 位机器是最常见的,并且他们期望 int 是 32 位而不是 32 位。 (也可能是,作为用户 ,首选 32 位数据,因为它需要更少 space,因此可以更快地访问。)

因此,如果您想确保获得 64 位整数,请使用 int64_t 而不是 int 来声明您的变量。如果您知道您的值适合 32 位或者您不关心大小,您可以使用 int 让编译器选择最有效的表示。

至于其他数据类型如struct,它们是由基本类型如int.

组成的

当你谈论编译器时,你一定对 build|host|target 有一个清晰的印象,即你正在构建的机器 (build),你正在为 (host) 构建的机器,以及GCC 将为 (target) 生成代码的机器,因为 "cross compiling" 与 "native compiling".

非常不同

关于问题 "who decide the sizeof datatype and structure",它取决于您告诉编译器为其构建二进制文件的目标系统。如果目标是 64 位,编译器会将 sizeof(long) 转换为 8,如果目标是 32 位机器,编译器会将 sizeof(long) 转换为 4。所有这些都已由您用于构建的头文件预定义你的程序。如果你读过你的 `$MAKETOP/usr/include/stdint.h',有 typedefs 来定义你的数据类型的大小。

为了避免大小差异造成的错误,Google coding style-Integer_Types 建议使用 int16_t、uint32_t、int64_t 等类型。这些在 <stdint.h>.

以上只是那些'Plain Old Data',比如int。如果说结构体,则另当别论,因为结构体的大小取决于packing alignment,结构体中每个字段的边界对齐方式,都会对结构体的大小产生影响。

它不是 CPU,也不是编译器,也不是操作系统。三者同时存在

编译器不能随便编造东西。它必须遵守操作系统提供的正确 ABI[1]。如果操作系统提供的结构和系统调用具有特定大小和对齐要求的类型,那么编译器就不能真正自由地实现自己的现实,除非编译器开发人员想要为操作系统提供的所有内容重新实现包装函数。那么操作系统的ABI也不能完全是凑出来的,它得做CPU上能合理做的事情。通常一个操作系统的 ABI 与同一 CPU 上其他操作系统的其他 ABI 非常相似,因为能够更容易地重用他们所做的工作(在编译器等方面)。

如果计算机同时支持 32 位和 64 位代码,操作系统仍然需要做一些工作来支持两种模式下的 运行 程序(因为系统必须提供两种不同的 ABI ).有些操作系统不这样做,而在那些操作系统上你别无选择。

[1] ABI 代表应用程序二进制接口。它是程序如何与操作系统交互的一组规则。它定义了程序如何存储在磁盘上以供操作系统运行、如何进行系统调用、如何 link 与库等。但是为了能够 link 到库,例如,您的程序和库必须就如何在您的程序和库之间进行函数调用(反之亦然)达成一致,并且为了能够进行函数调用,程序和库必须具有相同的堆栈布局概念,注册用法、函数调用约定等。对于函数调用,您需要就参数的含义达成一致,包括大小、对齐方式和类型的符号。

编译器决定基本类型的大小,以及结构的布局。如果一个库声明了任何类型,它将决定这些类型是如何定义的以及它们的大小。

然而,与现有标准的兼容性以及 link 对其他编译器生成的现有库的需求通常会迫使给定的实现做出某些选择。例如,语言标准说 wchar_t 必须比 16 位宽,而在 Linux 上它是 32 位宽,但在 Windows 上它一直是 16 位,所以Windows 的编译器都选择与 Windows API 兼容,而不是语言标准。 Linux 和 Windows 的许多遗留代码都假定 long 正好是 32 位宽,而其他代码则假定它的宽度足以容纳以秒为单位的时间戳或 IPv4 地址或文件偏移量或指针的位,并且(在一个编译器将 int 定义为 64 位宽并将 long 定义为 32 位宽之后)语言标准制定了一条新规则 int不能宽于 long.

因此,本世纪的主流编译器选择将int定义为32位宽,但历史上也有人将其定义为16位、18位、32位、64位等大小。某些编译器允许您选择 long 是否像某些遗留代码所假定的那样正好是 32 位宽,或者像其他遗留代码所假定的那样与指针一样宽。

这说明了您今天做出的假设(例如某种类型始终为 32 位宽)将来可能会反噬您。在向 32 位和 64 位代码的过渡中,C 代码库已经发生过两次这种情况。

但是您实际上应该使用什么

int 类型现在很少有用了。通常您可以使用一些其他类型来更好地保证您将获得什么。 (它确实有一个优点:没有 int 宽的类型可以自动加宽到 int,当您混合使用有符号和无符号类型时,这可能会导致一些非常奇怪的错误,并且 int是保证不短于int的最小类型。)

如果您正在使用特定的 API,您通常会希望使用与它相同的类型。标准库中有许多用于特定用途的类型,例如 clock_t 用于时钟滴答和 time_t 用于以秒为单位的时间。

如果您想要最快的至少 16 位宽的类型,那就是 int_fast16_t,还有其他类似的类型。 (除非另有说明,所有这些类型都在 <stdint.h> 中定义。)如果您想要最小的至少 32 位宽的类型,以将最多的数据打包到数组中,那就是 int_least32_t。如果你想要尽可能宽的类型,那就是 intmax_t。如果你知道你想要 32 位,并且你的编译器有这样的类型,它是 int32_t 如果你想要在 32 位机器上是 32 位宽而在 64 位机器上是在 64 位机器上的位宽,并且始终是存储指针的正确大小,即 intptr_t。如果你想要一个好的类型来进行数组索引和指针数学运算,那就是 <stddef.h> 中的 ptrdiff_t。 (这个在不同的 header 中,因为它来自 C89,而不是 C99。)

使用您真正想要的类型!

严格来说,100%,完全是编译器决定sizeof(int)的值。它不是系统和编译器的组合。它只是编译器(和 C/C++ 语言规范)。

如果您开发 iPad 或 iPhone 应用程序,编译器会在您的 Mac 上运行。 Mac 和 iPhone/iPac 使用不同的处理器。关于 Mac 的任何信息都没有告诉编译器 iPad.

上的 int 应该使用什么大小

处理器设计者决定可用的寄存器和指令、有效访问的对齐规则是什么、内存地址有多大以及so-on。

C 标准为 built-in 类型设定了最低要求。 "char" 必须至少为 8 位,"short" 和 "int" 必须至少为 16 位,"long" 必须至少为 32 位并且 "long long" 必须至少为至少 64 位。它还表示 "char" 必须等于程序可以寻址的最小内存单元,并且必须保持标准类型的大小顺序。

其他标准也可能有影响。例如 "single Unix specification" 的版本 2 表示 int 必须至少为 32 位。

现有代码终于产生了影响。移植已经够难了,没有人愿意让它变得比他们不得不做的更难。


将 OS 和编译器移植到新的 CPU 时,必须有人定义所谓的 "C ABI"。这定义了二进制代码如何相互通信,包括。

  • built-in 类型的大小和对齐要求。
  • 结构的打包规则(以及它们的大小)。
  • 参数是如何传递和返回的
  • 如何管理堆栈

一般来说,一旦 ABI 被定义为 CPU 系列和 OS 的组合,它就不会发生太大变化(有时 "long double" 等更模糊的类型的大小会发生变化) .改变它会带来一堆破损,但收益相对较小。

类似地,那些将 OS 移植到与现有平台具有相似特征的平台的人通常会选择与 OS 移植到的先前平台相同的大小。


在实践中 OS/compiler 供应商通常选择基本整数类型的几种大小组合之一。

  • "LP32":char为8位。 short 和 int 是 16 位,long 和 pointer 是 32 位。常用于 8 位和 16 位平台。
  • "ILP32":char为8位,short为16位。 int、long 和指针都是 32 位的。如果 long long 存在,则为 64 位。常用于32位平台。
  • "LLP64":char为8位。 short 是 16 位。 int 和 long 是 32 位。 long long 和 pointer 是 64 位。在 64 位上使用 windows.
  • "LP64":char为8位。 short 是 16 位。 int 是 32 位。 long、long long 和指针是 64 位。在大多数 64 位 unix-like 系统上使用。
  • "ILP64":char是8位,short是16位,int、long和pointer和long long都是64位。显然用于一些早期的 64 位操作系统,但现在很少见。

64 位处理器通常可以 运行 32 位和 64 位二进制文​​件。通常这是通过在 OS 中设置兼容层来处理的。因此,您的 32 位二进制文​​件使用与在 32 位系统上使用 运行ning 时相同的数据类型,然后兼容层转换系统调用,以便 64 位 OS 可以处理它们.

它是编译器,更准确地说是它的代码生成器组件。

当然,编译器是architecture-aware并做出适合它的选择。

在某些情况下,工作分两次执行,一次在 compile-time 由中间代码生成器执行,然后在 run-time 由 just-in-time 编译器执行。但这仍然是一个编译器。