谁决定任何数据类型或结构的大小(取决于32位还是64位)? 编译器还是处理器? 例如,对于32位系统,sizeof(int)是4个字节,而对于64位系统,sizeof(int)是8个字节。
我还读到使用32位和64位编译器编译时sizeof(int)是4个字节。
假设我的CPU可以运行32位和64位应用程序,谁将在决定编译器或处理器的数据大小方面发挥主要作用?
-
处理器执行机器代码,它不做任何决定
-
@StoryTeller的数字将用于认识论。 :-)
-
"假设我的CPU可以运行32位和64位应用程序,谁将在决定编译器或处理器的数据大小方面发挥主要作用?" CPU处理或不支持多少位并不重要。一旦编译了应用程序,就可以将"烘焙到应用程序的二进制文件中"的大小说成了。如果您的处理器无法处理二进制文件,则应用程序无法运行。
-
@Prof.Falken - 大声笑。嗯,这似乎合适:)
-
我不是这方面的专家,但是在WoW64的情况下,它只是将32位二进制文??件模拟为64位。所以在技术上它仍然加载一些东西作为64位,但然后只是显示为32位或类似的应用程序?
-
@CraigYoung,这是我没有考虑的问题的一个角度。好的评论,这将有利于未来观众的这个问题。
-
另请注意,int并不需要过多关注处理器架构。使用64位寄存器中的一半可用位,32位int工作正常。类似地,64位int可以在32位CPU上相当容易地模拟。然而,不那么灵活的一件事是指针的大小。即使这样,操作系统也可以允许32位应用程序在64位CPU NB上运行:请注意,指针的大小不一定与int的大小相同。
-
@Neijwiert,当64位Windows看到32位二进制文??件时,会启动WoW64,它基本上是所有系统Windows DLL,但是在32位版本中。所以64位安装的Windows有两个版本的大多数东西。许多64位Linux系统也是如此,我想,Macs。
-
@Prof.Falken对,但它不能一对一执行x86指令,对吧?
-
@Neijwiert,实际上是的,真正很酷的是,64位(Intel / AMD)CPU可以在(传统)32位和64位模式执行之间高效地切换模式。另请参阅stackoverflow.com/a/20885980/193892
-
stackoverflow.com/questions/2331751/ ---我在这里解释得很清楚。
-
另见unix和linux数据模型ilp32 lp64。 UNIX系统 - 64位和数据大小中立是非常好的阅读。
-
还有关系,int的大小是否取决于编译器和/或处理器?,C ++标准规定了int的大小,long类型是什么?
-
@Prof.Falken - 在你的一条评论中,你引用了Markdown中的评论。开始的那个,发生了什么......我不太了解这种联系。
-
int的大小当然不一定是64位平台的8字节。它适用于gcc x64,但是甚至不是gcc编译器的相同版本的所有平台,更不用说其他编译器了。如果你认为这样,你就会遇到麻烦。如果你需要足够宽的东西来指针,那就有size_t
-
@jww,我看到你输入了
的东西 strike>
-
有一种(可能)持有指针的类型,拼写uintptr_t或类似的东西。
它最终是编译器。无论CPU处理效率最高,编译器实现者都可以决定模拟他们认为合适的整数大小。也就是说,编写C(和C ++)标准,编译器实现者可以自由选择最快,最有效的方法。对于许多编译器,实现者选择将int保持为32位,尽管CPU本身非常有效地处理64位整数。
我认为这部分是为了增加对32位机器最常见并且期望int为32位而不再是int的程序的可移植性。 (也可能是,正如用户user3386109指出的那样,32位数据是首选,因为它占用的空间更少,因此可以更快地访问。)
因此,如果要确保获得64位整数,则使用int64_t而不是int来声明变量。如果您知道您的值将适合32位或您不关心大小,则使用int让编译器选择最有效的表示。
对于其他数据类型,例如struct,它们由基类型组成,例如int。 sub>
-
是的,但编译器还根据目标处理器设置大小。所以它是编译器,它使用有关处理器的信息。
-
@Justin,在关于编译器如何选择任何大小以提供最快或最有效结果的许多方法的部分中提到的。
-
或者32位用于节省内存并提高缓存性能。填充缓存行不需要很多64位整数。
-
"因此,如果你想确保得到64位整数,你可以使用long long或int64_t而不是int来声明你的变量......" - 无条件地使用long long会在某些平台上引起细微的问题,比如MinGW和Cygwin。 OP应检查数据模型并相应地生成typedef。如果数据模型是LP64,那么只需使用long int。
-
@jww,谢谢删除long long,我们不想宣传反模式。 :)
-
对于GCC来说,它始终是8,16,32,64位大小的良好进展的概念,在古代int通常是16位
-
int64_t不需要存在。 long long是必需的,并且总是至少64位宽;当硬件不直接支持它时,编译器将生成适当的代码。
-
int_fast64_t和int_least64_t是保证存在的stdint的两种可能替代方案(并且都有未签名的对应方)
-
@PeteBecker无法保证long long大于1个字节。在实践中,它至少是64位宽,就像在实践中int64_t存在一样。
-
@CortAmmon - 语言定义要求long long至少为64位宽。它有点难找,因为它在库部分。它来自C标准,要求LLONG_MIN必须不大于 - (2 ^ 63-1)且LLONG_MAX必须不小于2 ^ 63-1。
-
@PeteBecker那真是令人着迷。我以前从来没有必须交叉参考所有这些!
-
使用16位int"如果您知道您的值将适合32位或您不关心大小,则使用int让编译器选择最有效的表示。"是不好的建议。 2016年每年有100万个嵌入式处理器使用16位int。虽然Linux的程序不需要担心,因为操作系统推动32位int或更宽。
-
@CortAmmon - 为了完整性:char必须至少为8位宽; short至少16; int至少16; long至少32; long long至少64. signed char和所有unsigned变体相同。
-
关于为什么int在大多数64位编译器中是32位:64位编程模型:为什么选择LP64?
-
@chux:Posix需要32位整数,实际上Windows也是如此。限制自己使用Posix + Windows通常是一件合理的事情。
-
这很大程度上忽略了@Art正确地提出的标准(语言标准和处理器ABI)的存在。编译器编写者在许多领域都没有什么"余地",例如某些数据类型被"假定"的大小(@gyre指出了他们所做的几个)。
-
@MartinBonner请注意,到目前为止,2017年的大多数处理器都不属于Posix或Windows操作系统。嵌入式处理器每年数十亿。确实,OP的帖子标有操作系统,但C和标题问题的范围超出了这个范围。
-
@chux:好的。但处理器的数量并不是一个有趣的指标。编写软件的大多数人都是为Posix或Windows编写的(而那些不是,那些用于编写32位int的嵌入式处理器的人)
它不是CPU,也不是编译器,也不是操作系统。这三个是同时进行的。
编译器不能只是搞砸了。它必须遵守操作系统提供的正确的ABI [1]。如果操作系统提供的结构和系统调用具有某些大小和对齐要求的类型,那么除非编译器开发人员想要为操作系统提供的所有内容重新实现包装函数,否则编译器实际上无法自行构建自己的实际情况。然后操作系统的ABI不能完全组成,它必须做可以在CPU上合理完成的事情。通常,一个操作系统的ABI与同一CPU上其他操作系统的其他ABI非常相似,因为它们更容易重用他们所做的工作(在编译器等)。
如果计算机同时支持32位和64位代码,则操作系统仍需要完成工作以支持在两种模式下运行程序(因为系统必须提供两种不同的ABI)。有些操作系统没有这样做,而有些操作系统则没有选择。
[1] ABI代表应用程序二进制接口。它是程序如何与操作系统交互的一组规则。它定义了程序如何存储在磁盘上以便操作系统可以运行,如何进行系统调用,如何与库链接等等。但是为了能够链接到库,例如,您的程序和库必须同意关于如何在程序和库之间进行函数调用(反之亦然),并且为了能够进行函数调用,程序和库必须具有相同的堆栈布局,寄存器使用,函数调用约定等概念。对于函数调用,您需要就参数的含义达成一致,包括类型的大小,对齐和签名。
-
ABI还包含调用约定,这是一组程序调用函数的规则。它还包含处理器指定的约束(例如,各种原始数据类型的对齐约束)
-
不需要编译器支持程序可以与外部世界中的任何内容交互的任何方法,而无需通过与编译器捆绑在一起的库函数。没有什么能禁止使用36位的补码整数类型来实现针对x64的实现(执行任何移位和模拟操作所需的屏蔽操作)。如果某人有一些他们想要运行的Univac代码但是他们没有一个可用的36位系统,那么这样的实现可能真的很有用。
-
@supercat我从来没有说过有这样的要求。我刚刚描述了现实。
-
Upvoted是因为它给出了"非常基于实际现实"的角度。
-
@supercat虽然ISO中没有要求该程序能够与外部世界进行交互,但我认为这点实际上这将成为编译器选择其大小的主要影响因素,以同样的方式虽然x64编译器可以提供19位char和41位int s填充到57 ...但它们不会,除非编写仿真器或未定义行为 - 残忍者。
-
这是完全错误的。它完全是编译器。编译器可能会选择适合处理器和操作系统的大小,但这完全是编译器的决定。
-
@MartinBonner好吧,使用那个参数不是编译器,而是你。因为您可以选择编译器。除了,也许没有自由意志,那就是选择的宇宙。大爆炸设置了参数,这些参数导致您选择一个编译器,该编译器由参数使他们为类型选择特定大小的人编写。另一方面,在我居住的实际情况中,我们有编译器以有用的方式与他们的环境进行交互,人们不会浪费时间来构建无用的编译器,但有些人仍然浪费时间来争论它可以完成。
-
实际的正确答案,谢天谢地。可能值得承认的是,一些ABI允许选择具有不同大小的指针,数据布局的"存储器模型"(特别是在RISC处理器上,其中加载更大的指针需要额外的指令,和/或存在具有较小偏移的特殊寻址模式,更高效)。
-
@MartinBonner Art说得对,编译器通常遵循标准ABI。 ABI设计将受到处理器设计的极大限制,而一些ABI是由CPU创建者设计的,尽管有些是特定于操作系统的。
-
@WillCrawford有很多用于Windows的x64编译器使用了一些与Windows x64调用约定完全不同的组合ABI。因为他们决定引入整个Linux ABI包,并且完全正是这样做的。
-
......从那时起,人们一直在修理的错误; o)
-
@Leushenko:在C离开轨道之前,我一直希望编译器可以朝着允许"编译器方便"数据类型和保证格式数据类型的方向发展。具有讽刺意味的是,早期C的一个优点是,人们可以在数据类型符合要求的机器上使用结构进行数据格式转换,而不是通过告诉编译器让程序员编写可移植代码的方向发展。他们需要什么样的布局,编译器已经朝着要求程序员对所有内容进行字节解析的方向发展......
-
......除非他们禁用批发优化。
-
为了迂腐,ABI是应用程序二进制接口,它本身并不限制类型的大小。如果你关心的只是能够链接到ABI,你可以更改标题以用int32_t替换所有int声明。他们将链接到使用现有ABI编译的库很好。实际中的实际约束是旧编译器的替代品需要与现有源代码兼容,并具有所有隐含的假设。
-
这个答案是不正确的。你完全可以编写一个遵循不同调用约定的gcc分支。它无法链接到系统库 - 但它可以创建自己的运行时库并将其粘贴到不同的目录中。然后它不能链接其他库,除非它们遵循你的新约定 - 所以,根据你自己的运行时重新编译它们并将新版本放在同一目录中,这已经完成了好几次。 @Joker_vD给出了一个例子,Linux上的x32替代ABI是另一个例子。
-
@WillCrawford关键不是这是一个好主意或一个坏主意;重点是它已经完成,所以它必须是可能的。 (至少如果你只关心FOSS库。)
严格地说,100%完全是编译器决定sizeof(int)的值。它不是系统和编译器的组合。它只是编译器(和C / C ++语言规范)。
如果您开发iPad或iPhone应用程序,则可以在Mac上运行编译器。 Mac和iPhone / iPac使用不同的处理器。没有任何关于你的Mac告诉编译器应该在iPad上使用int的大小。
-
如果你打算反对Art的答案,那么你就过度了。我想他的回答是指目标系统,而不是交叉编译器运行的主机,而你谈的是主机系统,当然,它与目标上的数据类型大小无关。
-
这根本不是真的。至少你承认"语言规范",但是每个系统都有一个标准(ABI),它的大小是默认的"int",参数是如何传递给函数的......等等。对于iOS应用程序,这个Apple开发人员指南说明了这一点:
-
(引用)当不同的代码片段必须一起工作时,它们必须遵循关于代码应如何行动的标准约定的约定。约定包括常见数据类型的大小和格式,以及一段代码调用另一段代码时使用的指令。编译器基于这些约定实现,以便它们可以发出一起工作的二进制代码。这些约定统称为应用程序二进制接口(ABI)。
-
同样,编译器决定是否遵循该约定。而"ABI"只是一个流行语的缩写。几十年前,我们将其称为"召集会议"。这些调用约定对编译器没有任何规定。编译器应该遵循它们,但情况并非总是如此。见证了Windoze开发人员必须处理的C与Pascal调用约定。
-
Apple改变了他们的编译器以遵循ABI ......
-
注意:他们改变了他们的编译器。这是一个选择,而不是系统强加的要求。
-
一切都是选择(包括遵循ANSI C标准,许多编译器默认不这样做),每个选择都有价格。如果你把工作放在里面是绝对可以构建一个C编译器,它忽略了它所针对的平台的标准约定(注意:并不像它运行的平台那样),并将所有标准库函数包装或重新实现为使它们与您的编译器兼容。仅仅因为你可以做某事虽然不能让它成为一个好主意。
处理器设计者确定哪些寄存器和指令可用,有效访问的对齐规则是什么,内存地址有多大等等。
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。)
使用你真正的意思!
-
"语言标准说wchar_t必须宽于16位" - 章和节?我很确定它在任何版本中都没有说,至少没有明确说明。标准中关于它应该有多宽的模糊性是为什么引入了char16_t和char32_t。
-
该标准没有说wchar_t必须宽于8位,也没有指定使用什么字符集(系统原则上可以使用16位wchar作为特定于语言环境的东亚代码页,或者仅支持BMP),但很明显,使用可变长度或有状态编码是不可接受的。
-
@ Random832标准说wchar_t是一个整数类型,其值范围可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码。"在给定实现不具有的意义上,您是正确的必须提供支持Unicode甚至ASCII的任何语言环境。但是,该标准确实需要支持UTF-8,UTF-16和UCS-32字符串以及char32_t类型。如果您想与Microsoft的语言环境兼容:setlocale(".1200");单独需要支持所有Unicode。 Windows API需要UTF-16。
-
@ Random832因此,任何主流实现必须支持至少一个Unicode语言环境甚至与操作系统兼容,并且能够编译使用Unicode的应用程序是2018年的事实上的要求。理论上,这将意味着wchar_t在Windows编译器上必须足够宽以存储至少1,112,064个不同的值,因此必须至少为21位宽。在实践中,微软在上个世纪将wchar_t定义为16位宽,将其从UCS-2更改为UTF-16,并拒绝破坏所有内容。所以每个使用Windows API的人都会这样做。
-
我认为你说它听起来像编译器是一个穷人,受压迫的少数民族被语言标准欺负; o)。但是,严肃地说,处理器架构对例如类型的最小对齐要求,例如64位整数是否需要与"64位边界"对齐,或32,或者根本不对齐;和ABI标准存在是有原因的(所以你对互操作性有希望)。可能是系统上的第一个编译器最终定义了ABI,但它仍然非常局限于它可以做出的决策。
-
@WillCrawford我不确定你究竟是什么意思 - 就像我说的那样,语言标准本身只说int必须至少16位宽,至少宽short且不宽于< x10>,我给出了一个类型定义的扩展示例,该类型定义直接违反了与OS API兼容的语言标准。但实际上,int通常受系统库,需要支持遗留代码或两者的约束。任何可以自由选择的编译器都必须选择它将破坏的几十年前的代码。
-
我觉得你描述它的方式让它听起来像"编译器"是所有人的仲裁者,而这并不是真正的整体情况......我试图对此保持幽默,因为我可以看到你努力工作以创造一个良好的书面答复(!)。这部分是问题的错误 - 它根本没有提到ABI并要求二元选择,所以你的答案肯定不是"完全错误"。
-
@WillCrawford我编辑了一下,希望对编译器实现者面临的约束更加清楚一点。
-
@WillCrawford即使在最新编辑之前,我确实将wchar_t的示例作为为与API兼容而定义的类型。 (从技术上讲,如果ABI是重要的,它可能只是让程序员使用char16_t*并仍然能够与其他库链接,并将wchar_t重新定义为char32_t。)但我希望现在没有歧义。
-
它更好(之前也不错)。我希望你和@Art能够结合力量:o)
它是编译器,更准确地说是它的代码生成器组件。
当然,编译器是体系结构感知的,并做出适合它的选择。
在某些情况下,工作分两次执行,一次是在编译时由中间代码生成器执行,另一次是在运行时由即时编译器执行。但这仍然是一个编译器。
当您谈论编译器时,您可以获得关于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。如果你谈论一个结构,还有另一个故事,因为结构的大小取决于打包对齐,结构中每个字段的边界对齐,这将影响结构的大小。