关于c ++:谁决定任何数据类型或结构的大小(取决于32位还是64位)?

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

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

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

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


它最终是编译器。无论CPU处理效率最高,编译器实现者都可以决定模拟他们认为合适的整数大小。也就是说,编写C(和C ++)标准,编译器实现者可以自由选择最快,最有效的方法。对于许多编译器,实现者选择将int保持为32位,尽管CPU本身非常有效地处理64位整数。

我认为这部分是为了增加对32位机器最常见并且期望int为32位而不再是int的程序的可移植性。 (也可能是,正如用户user3386109指出的那样,32位数据是首选,因为它占用的空间更少,因此可以更快地访问。)

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

对于其他数据类型,例如struct,它们由基类型组成,例如int


它不是CPU,也不是编译器,也不是操作系统。这三个是同时进行的。

编译器不能只是搞砸了。它必须遵守操作系统提供的正确的ABI [1]。如果操作系统提供的结构和系统调用具有某些大小和对齐要求的类型,那么除非编译器开发人员想要为操作系统提供的所有内容重新实现包装函数,否则编译器实际上无法自行构建自己的实际情况。然后操作系统的ABI不能完全组成,它必须做可以在CPU上合理完成的事情。通常,一个操作系统的ABI与同一CPU上其他操作系统的其他ABI非常相似,因为它们更容易重用他们所做的工作(在编译器等)。

如果计算机同时支持32位和64位代码,则操作系统仍需要完成工作以支持在两种模式下运行程序(因为系统必须提供两种不同的ABI)。有些操作系统没有这样做,而有些操作系统则没有选择。

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


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

如果您开发iPad或iPhone应用程序,则可以在Mac上运行编译器。 Mac和iPhone / iPac使用不同的处理器。没有任何关于你的Mac告诉编译器应该在iPad上使用int的大小。


处理器设计者确定哪些寄存器和指令可用,有效访问的对齐规则是什么,内存地址有多大等等。

C标准设置了内置类型的最低要求。"char"必须至少为8位,"short"和"int"必须至少为16位,"long"必须至少为32位,"long long"必须至少为64位。它还说"char"必须与程序可以解决的最小内存单元等效,并且必须保持标准类型的大小顺序。

其他标准也可能产生影响。例如,"单一Unix规范"的版本2表示int必须至少为32位。

最后,现有代码会产生影响。移植已经很难了,没有人想让它变得更难。

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

  • 内置类型的大小和对齐要求。
  • 结构的包装规则(以及它们的尺寸)。
  • 如何传递和返回参数
  • 如何管理堆栈

一般来说,一旦ABI被定义为CPU系列和OS的组合,它的变化不大(有时像"长双"变化这样的模糊类型的大小)。改变它会带来一堆破损,收益相对较小。

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

实际上,OS /编译器供应商通常会选择基本整数类型的几种大小组合之一。

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

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


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

但是,通常情况下,与现有标准的兼容性以及链接到其他编译器生成的现有库的需要迫使给定的实现做出某些选择。例如,语言标准说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,还有其他类似的类型。 (除非另有说明,否则所有这些类型都在中定义。)如果您想要至少32位宽的最小类型,要将大多数数据打包到数组中,那么int_least32_t。如果你想要最宽的类型,那就是intmax_t。如果你知道你只需要32位,并且你的编译器有类似的类型,那么int32_t如果你想在32位机器上使用32位宽,在64位机器上需要64位宽,并且总是正确的大小来存储指针,即intptr_t。如果你想要一个好的类型来做数组索引和指针数学,那么ptrdiff_t来自。 (这个是在不同的标题中,因为它来自C89,而不是C99。)

使用你真正的意思!


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

当然,编译器是体系结构感知的,并做出适合它的选择。

在某些情况下,工作分两次执行,一次是在编译时由中间代码生成器执行,另一次是在运行时由即时编译器执行。但这仍然是一个编译器。


当您谈论编译器时,您可以获得关于build|host|target的清晰图像,即您正在构建的机器(构建),您正在构建的机器(主机),以及GCC将为其生成代码的机器(目标),因为"交叉编译"与"本机编译"非常不同。

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

为了避免由大小差异造成的错误,Google编码样式-Integer_Types建议使用类型如int16_t,uint32_t,int64_t等。这些类型在中定义。

上面只是那些"普通旧数据",比如int。如果你谈论一个结构,还有另一个故事,因为结构的大小取决于打包对齐,结构中每个字段的边界对齐,这将影响结构的大小。