size_t是表示大小的类型吗?

33
“size_t”是否代表编译代码的机器的字长?我使用g++解析时,编译器将“size_t”视为“long unsigned int”。是编译器内部选择了“size_t”的大小,还是“size_t”在调用编译器之前已经在“stddef.h”中进行了预处理宏定义为字长?或者我的理解完全偏离了轨道?

4
不用担心,这是一个合理的问题。 - scones
你想要做什么? - Nemo
3
我只是试图理解它是什么。 - gone
2
那么,下面标准中的引用可以精确地回答您的问题。只要无符号整数类型足够大以表示任何对象的大小,您的编译器可以使用任何size_t类型,这是您在编写C或C++代码时能够假定的全部内容。 - Nemo
5
虽然所有回答都是正确的,但我没有看到任何人提到size_t往往是机器的字长。(而且“往往”字面上意思是:几乎所有情况下——也就是说,我从来没有听说过任何一个不是这样的环境。) - Mysticial
显示剩余2条评论
6个回答

24
在C ++标准中,[support.types] (18.2) / 6:"size_t类型是一个实现定义的无符号整数类型,足够大以包含任何对象的字节大小。" 这可能与“字大小”相同,不管这意味着什么。

6
@ZacharyO'Keefe,我认为这不是你的意思。按定义,一个字节是可以寻址的最小内存单元。 - Seth Carnegie
2
@ZacharyO'Keefe:标准中的引用并没有提到“最小”和机器有多少内存。它足够大,可以容纳实现可以分配的任何对象的大小;这是您可以假设的全部内容。 - Nemo
2
size_t 不一定是指针的大小。虽然通常情况下是这样,但在一个32位分段x86模型中,指针大小为48位,但一个“对象”仍然只能是32位,因此 size_t 将是一个32位的值。 - Mats Petersson
3
@ZacharyO'Keefe - 关于"bounded"的问题-公平的问题。编译器不知道运行程序的系统有多少内存,因此决定最大可能对象的大小并不取决于可用内存的数量。如果没有足够的内存创建对象,则程序将在运行时失败,有时以神秘的方式。 - Pete Becker
1
我说的是“分段x86”,意思是每个内存块都与一个段相关联,这意味着指针将具有16位段和32位“段内偏移量”。这是一种完全有效但相当不寻常的处理器运行模式。 - Mats Petersson
显示剩余12条评论

15
不是的;size_t并不一定是您所谓的运行代码的机器(在交叉编译的情况下)或编译代码的机器(在正常情况下,代码将在与编译代码相同类型的机器上运行)的“字大小”。它是一个无符号整数类型,足够大以容纳实现可以分配的最大对象的大小(以字节为单位)。

sizeofsize_t的历史

我不知道确切的size_t引入时间,但在1979年至1989年之间。K&R《C语言程序设计》第1版于1978年没有提到size_t。Unix程序员手册第7版从1979年起完全没有提到size_t。Kernighan和Pike的书《UNIX编程环境》于1984年在索引中没有提到size_t(也没有出乎我的意料地提到malloc()free()),但这只是指示性的,而不是决定性的。C89标准当然有size_t

C99理由文件记录了一些关于sizeof()size_t的信息:

6.5.3.4 sizeof运算符
在使用malloc和fread等函数时,sizeof(char)恰好为1是正确的基础。实际上,这意味着在C术语中,一个字节是最小的存储单元,即使该单元宽度为36位;并且所有对象都由整数个这些最小单元组成。如果内存是按位寻址,则也适用。C89像K&R一样将sizeof运算符的结果定义为无符号整数类型的常量。通常的实现和使用常常假定结果类型是int。依赖于此行为的旧代码从未能够移植到将结果定义为非int类型的实现。C89委员会认为更改语言以保护不正确的代码是不合适的。
sizeof的类型(无论是什么)被发布(在库头文件stddef.h中)为size_t,因为程序员能够引用这种类型是有用的。这要求隐式地将size_t限制为现有无符号整数类型的同义词。还要注意,尽管size_t是无符号类型,但sizeof不涉及任何算术运算或转换,如果大小过大而无法表示为size_t,则不会产生模数行为,从而打消了最大可声明对象可能太大而无法跨越的概念,即使在C89中使用unsigned long或C99中使用uintmax_t也是如此。这也限制了可以在数组中声明的最大元素数,因为对于任何由N个元素组成的数组a,
N == sizeof(a) / sizeof(a[0])
因此,size_t也是数组大小的便捷类型,并在几个库函数中使用。
7.17公共定义
stddef.h是一个头文件,用于提供与库广泛结合使用的几种类型和宏的定义:ptrdiff_t、size_t、wchar_t和NULL。包含任何引用其中一个宏的头文件也将定义它,这是通常库规则的一个例外,即每个宏或函数属于恰好一个头文件。
请注意,这里特别提到了<stddef.h>是由C89委员会发明的。我没有找到说明size_t也是由C89委员会发明的文字,但如果不是的话,它应该是对C中最近发展的一种编码。
在对bmarguliesanswer发表评论时,vonbrand说:“这绝对是ANSI-C的一种创新”。我很容易相信它是最初的ANSI(ISO)C的创新,尽管理由没有明确说明。

1
交叉编译与何相关? - R.. GitHub STOP HELPING ICE
2
这个问题询问 size_t 的大小是否与编译代码的机器的字长有关。如果你在 64 位机器上进行 8 位或 16 位微处理器的交叉编译,那么程序中 size_t 的大小很可能与编译机器上的字长毫不相干 — 这就是我提到交叉编译的原因。 - Jonathan Leffler
1
@JonathanLeffler:“它是一个无符号整数类型,足够大以容纳实现可以分配的最大对象的大小(以字节为单位)”。由于您只能分配到地址空间,因此“size_t”必须受地址空间中有多少内存的限制? - gone
@ZacharyO'Keefe:通常情况下,您是正确的,但声称“必须”是危险的,除非您根本不需要首先提出问题(即使您认为自己已经完全掌握了答案,声称“必须”也可能是危险的)。 C89标准仅表示<stddef.h>头文件定义了一个类型'_size_t,它是sizeof运算符结果的无符号整数类型'。 C99将'integral'更改为'integer',而C11与C99相同。 - Jonathan Leffler
标准对于 sizeof 运算符的解释并不是很详细;它说:'sizeof 的结果值是由实现定义的,其类型(一个无符号整数类型)是在 <stddef.h>(和其他头文件)中定义的 size_t。' - Jonathan Leffler
显示剩余2条评论

3
不一定。C ISO规范(§17.1/2)将size_t定义为“sizeof运算符结果的无符号整数类型”。换句话说,size_t必须足够大,以容纳由sizeof产生的任何表达式的大小。这可能是机器字长,但它也可能小得惊人(例如,如果编译器限制了数组或对象的最大大小),或者大得惊人(如果编译器允许您创建如此巨大的对象,以至于单个机器字无法存储该对象的大小)。希望这有所帮助!

啊,所以size_t是由我的编译器实现设置的?由于我可以分配的最大内存量明显受到物理内存的限制,那么size_t是否受到我可供地址空间使用的物理内存量的限制呢? - gone
@ZacharyO'Keefe- 这完全取决于编译器的自主权。编译器可以合理地使size_t足够大,以容纳任意两个物理地址之间的差异(对象的结束地址和起始地址的差异给出了其大小)。另一方面,你可以想象一个奇怪的设置,其中有一个128位的地址空间,但是分配器不能分配超过4GB的内存,在这种情况下,机器可以使用32位整数作为size_t。唯一的方法就是查看编译器文档。 - templatetypedef
谢谢,这确实有帮助。那么如果我尝试分配一个2^size_t个字符的数组(静态或动态分配),会发生什么?(即,比size_t可以表示的多1个)。编译器会报错吗? - gone
1
@ZacharyO'Keefe:在静态情况下,编译器可能会抱怨。在动态情况下,它可能会绕过去,最终分配0。 - icktoofay
1
@ZacharyO'Keefe- malloc(C)和operator new(C ++)的参数类型为size_t,因此如果您尝试传递较大的值,则数字将溢出。 - templatetypedef

1

size_t最初只是在sys/types.h中的typedef(传统上在Unix / Linux上)。它被认为足够大,例如,对于文件的最大大小或使用malloc进行的最大分配。然而,随着时间的推移,标准委员会掌握了它,因此它被复制到许多不同的头文件中,并每次使用自己的#ifdef保护来防止多重定义。另一方面,具有非常大潜在文件大小的64位系统的出现使其角色变得有些模糊。因此,它有点像古文物。

现在的语言标准将其列为存储在stddef.h中。它与硬件字长没有必要的关系,也没有编译器魔法。请参阅其他答案,了解这些标准对其大小的规定。


5
这在 stddef.h 中,是标准中的内容,不特定于任何平台。 - Rapptz
这是我一直在阅读的内容..但我不知道那是什么意思? - gone
当您包含许多C头文件之一时,包括<stddef.h><string.h><stdio.h><stdlib.h>,类型size_t将被定义。 - Jonathan Leffler
1
一个符合标准的 C 实现很难包含 <sys/types.h> 来定义 size_t,因为这样会暴露太多其他保留给用户的符号。 - Jonathan Leffler
@bmargulies:你现在知道,在Windows中没有sys\types.h。 - Cheers and hth. - Alf
显示剩余4条评论

0

这些定义都是实现定义的。如果我需要最佳猜测大小,我会使用sizeof(char *)或者sizeof(void *)。这样做能够给出软件使用的表面字长...但硬件实际上可能不同(例如,32位系统可能通过软件支持64位整数)。

此外,如果您是C语言的新手,请查看stdint.h以获取有关整数大小的各种材料。


0

尽管定义并没有直接说明size_t是什么类型,也没有要求最小大小,但它间接地给出了一些好的提示。一个size_t必须能够包含任何对象的字节大小,换句话说,它必须能够包含可能的最大对象的大小。

可能的最大对象是一个大小等于整个可用地址空间的数组(或结构)。无法以有意义的方式引用一个更大的对象,除了交换空间的可用性外,没有理由需要它变得更小。

因此,根据定义的措辞,在32位架构上,size_t必须至少为32位,在64位系统上,size_t必须至少为64位。当然,实现可以选择更大的size_t,但这通常不是这种情况。


1
§7.8.13的第2段设置了size_t的最小最大值为65535。 - icktoofay
2
你的第二段(以及所有基于此的内容)假设所有实现都必须支持像整个地址空间一样大的对象。据我所知,情况并非如此。你有参考资料吗? - user395760
@delnan:需要实现接受一个带有“大于零的整数常量表达式”边界的数组声明。这种措辞原则上允许任何大小的边界,只要它是一个常量(甚至是一个有500位数字的数字?)。但当然,没有比地址空间更大的意义,因为没有任何有意义的方法来处理它。在实践中,您当然永远无法分配这样的对象(程序代码必须放在某个地方,再加上碎片等),但这并不排除理论上可能存在这样大小的对象的可能性。 - Damon
好的,但是由于任意实现定义的限制总会存在(至少是目标平台的地址空间,正如您所指出的),我不明白为什么这个限制是可以接受的,但是比这个限制小一半的限制就无效了。我认为C和C++标准甚至不涉及地址空间等概念。 - user395760
我不认为任何标准考虑了像地址空间这样的东西。这只是一个你不可避免地遇到的实际限制。可能有某个扭曲的条款(我不知道),允许一些任意的下限,但我不认为这适用于类型的实际大小。当然,实现从未要求实际上创建任何大小的对象(出于任何原因),例如,即使对于小于您的地址空间1/4大小的数组,通常也会得到bad_alloc,但这与不支持该类型并不相同。 - Damon
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接