unsigned int 与 size_t

Posted

技术标签:

【中文标题】unsigned int 与 size_t【英文标题】:unsigned int vs. size_t 【发布时间】:2010-09-13 00:10:03 【问题描述】:

我注意到现代 C 和 C++ 代码似乎几乎无处不在使用 size_t 而不是 int/unsigned int - 从 C 字符串函数的参数到 STL。我很好奇这样做的原因及其带来的好处。

【问题讨论】:

【参考方案1】:

size_t 类型是无符号整数类型,它是 sizeof 运算符(和 offsetof 运算符)的结果,因此它保证足够大以包含系统最大对象的大小可以处理(例如,8Gb 的静态数组)。

size_t 类型可能大于、等于或小于unsigned int,您的编译器可能会对其进行优化假设。

您可以在 C99 标准的第 7.17 节中找到更准确的信息,该草案的草稿在 Internet 上以pdf 格式提供,或者在 C11 标准的第 7.19 节中,也以pdf draft 的形式提供。

【讨论】:

不。想想 x86-16 的大(不是大)内存模型:指针很远(32 位),但单个对象限制为 64k(因此 size_t 可以是 16 位)。 “最大对象的大小”的措辞并不差,但绝对正确。一个对象的六个可能比地址空间更有限。 “您的编译器可能会对此做出假设”:我希望编译器知道 size_t 可以表示的值的确切范围!如果没有,谁来做? @Marc:我认为更重要的是编译器可能能够做某事用这些知识。 我只是希望这种日益流行的类型不需要包含头文件。【参考方案2】:

Classic C(Brian Kernighan 和 Dennis Ritchie 在 The C Programming Language,Prentice-Hall,1978 年描述的 C 的早期方言)没有提供 size_t。 C 标准委员会引入了size_t 来消除可移植性问题

Explained in detail at embedded.com (with a very good example)

【讨论】:

另一篇解释 size_t 和 ptrdiff_t 的好文章:viva64.com/en/a/0050【参考方案3】:

简而言之,size_t 永远不会是负数,它最大限度地提高了性能,因为它的 typedef'd 是无符号整数类型,它足够大 - 但不是太大 - 以表示最大可能对象的大小目标平台。

大小永远不应该是负数,事实上size_t 是一个无符号类型。此外,因为size_t 是无符号的,您可以存储大约是相应有符号类型的两倍大的数字,因为我们可以使用符号位来表示幅度,就像无符号整数中的所有其他位一样。当我们获得更多位时,我们将可以表示的数字范围乘以大约两倍。

那么,您问,为什么不直接使用unsigned int?它可能无法容纳足够大的数字。在unsigned int 是32 位的实现中,它可以表示的最大数字是4294967295。某些处理器(例如 IP16L32)可以复制大于 4294967295 字节的对象。

那么,你问,为什么不使用unsigned long int?它会在某些平台上造成性能损失。标准 C 要求 long 至少占用 32 位。 IP16L32 平台将每个 32 位长实现为一对 16 位字。这些平台上的几乎所有 32 位运算符都需要两条指令,甚至更多,因为它们使用两个 16 位块中的 32 位。例如,移动 32 位长度通常需要两条机器指令 - 一条用于移动每个 16 位块。

使用size_t 可以避免这种性能损失。根据this fantastic article,“类型size_t 是一个typedef,它是一些无符号整数类型的别名,通常是unsigned intunsigned long,但甚至可能是unsigned long long。每个标准C 实现都应该选择无符号整数这足以表示目标平台上最大可能对象的大小——但不会超过所需的大小。”

【讨论】:

很抱歉这么久才对此发表评论,但我只需要确认 unsigned int 可以容纳的最大数字 - 也许我误解了你的术语,但我认为最大的数字是 unsigned int 可以容纳的是 4294967295,65356 是 unsigned short 的最大值。 如果你的 unsigned int 占用 32 位,那么是的,它可以容纳的最大数是 2^32 - 1,即 4294967295 (0xffffffff)。您还有其他问题吗? @Mitch:unsigned int 中可以表示的最大值可以并且确实因系统而异。它需要至少 65536,但通常是4294967295,在某些系统上可能是18446744073709551615 (2**64-1)。 16 位无符号整数可以包含的最大值是 65535,而不是 65536。65536 的一个小而重要的区别与 16 位无符号整数中的 0 相同。 @gnasher729:你确定 C++ 标准吗?搜索了一段时间后,我的印象是他们只是删除了关于整数范围的所有绝对保证(不包括unsigned char)。该标准似乎在任何地方都没有包含字符串“65535”或“65536”,并且“+32767”仅出现在 (1.9:9) 的注释中,因为 可能int 中可表示的最大整数;即使INT_MAX 不能小于该值,也不能保证!【参考方案4】:

size_t 类型是 sizeof 运算符返回的类型。它是一个无符号整数,能够以字节为单位表示主机支持的任何内存范围的大小。它(通常)与 ptrdiff_t 相关,因为 ptrdiff_t 是一个有符号整数值,因此 sizeof(ptrdiff_t) 和 sizeof(size_t) 相等。

在编写 C 代码时,您应该始终在处理内存范围时使用 size_t。

另一方面,int 类型基本上定义为主机可以用来最有效地执行整数运算的(有符号)整数值的大小。例如,在许多较旧的 PC 类型计算机上,值 sizeof(size_t) 将为 4(字节),但 sizeof(int) 将为 2(字节)。 16 位算术比 32 位算术快,尽管 CPU 可以处理高达 4 GiB 的(逻辑)内存空间。

仅当您关心效率时才使用 int 类型,因为它的实际精度很大程度上取决于编译器选项和机器架构。特别是 C 标准指定了以下不变量: sizeof(char)

注意:这与 Java 中的不同(它实际上指定了每个类型 'char'、'byte'、'short'、'int' 和 'long' 的位精度)。

【讨论】:

int 的实际定义是它在 16 台机器上是 16 位,在任何更大的机器上是 32 位。已经编写了太多代码,假设 int 是 32 位宽,现在要更改它,因此人们应该始终使用 size_t 或 ,uint8,16,32,64_t 如果他们想要特定的东西 - - 作为预防措施,人们应该总是使用这些,而不是整数类型。 “它是一个无符号整数,能够以字节表示主机支持的任何内存范围的大小。” --> 编号size_t 能够表示任何单个对象的大小(例如:数字、数组、结构)。整个内存范围可能超过size_t "在编写 C 代码时,无论何时处理内存范围,您都应该始终使用 size_t。" -- 这意味着每个数组的每个索引都应该是size_t - 我希望你不是那个意思。大多数时候,我们不处理地址空间的基数+可移植性甚至很重要的数组。在这些情况下,您会选择size_t。在所有其他情况下,您从(有符号)整数中取出索引。因为与其他情况下可能出现的可移植性问题相比,unsigneds 的未预料到的下溢行为引起的混乱(没有警告)更常见且更糟糕。 @johannes_lalala 我希望你不是那个意思。没有其他类型可以保证足够大以容纳最大的有效数组索引。有符号整数在溢出或下溢时不好,因为它们会导致 UB,而无符号上溢和下溢不会导致 UB,但定义明确。 size_t 应该用于所有非负数组索引。 是的,我做到了。你几乎不需要大指数,我 20 年来从来不需要。如果您认为您可能会遇到大索引,请使用 size_t。在所有其他情况下,不要。考虑这个常见的范围验证:index = x - y.. later: if (index < 0) -> fail, otherwise z = arr[index].. 如果在这里使用无符号整数会发生什么。顺便说一句,这也是 c++ 委员会对这个话题的官方立场【参考方案5】:

类型 size_t 必须足够大以存储任何可能对象的大小。 Unsigned int 不必满足该条件。

例如在 64 位系统中 int 和 unsigned int 可能是 32 位宽,但 size_t 必须足够大以存储大于 4G 的数字

【讨论】:

“object”是标准使用的语言。 我认为 size_t 只有在编译器可以接受类型 X 以使 sizeof(X) 产生大于 4G 的值时才需要那么大。大多数编译器会拒绝,例如typedef unsigned char foo[1000000000000LL][1000000000000LL],甚至 foo[65536][65536]; 如果超过了记录在案的实现定义的限制,都可能被合法地拒绝。 @MattJoiner:措辞很好。 “对象”一点也不含糊,而是定义为“存储区域”。【参考方案6】:

glibc 手册 0.02 的摘录在研究该主题时也可能是相关的:

2.4 版之前的 GCC 的 size_t 类型和版本存在潜在问题。 ANSI C 要求 size_t 始终是无符号类型。为了与现有系统的头文件兼容,GCC 定义了 stddef.h' to be whatever type the system'ssys/types.h 中的 size_t 将其定义为。大多数在 `sys/types.h' 中定义 size_t 的 Unix 系统将其定义为有符号类型。库中的一些代码依赖于 size_t 是一个无符号类型,如果它是有符号的,将无法正常工作。

期望 size_t 为无符号的 GNU C 库代码是正确的。 size_t 作为有符号类型的定义是不正确的。我们计划在 2.4 版本中,GCC 将始终将 size_t 定义为无符号类型,而 fixincludes' script will massage the system'ssys/types.h' 则不会与此冲突。

与此同时,我们通过明确告诉 GCC 在编译 GNU C 库时对 size_t 使用无符号类型来解决这个问题。 `configure' 将自动检测 GCC 用于 size_t 的类型,并在必要时安排覆盖它。

【讨论】:

【参考方案7】:

如果我的编译器设置为 32 位,size_t 只不过是 unsigned int 的 typedef。如果我的编译器设置为 64 位,size_t 只不过是 unsigned long long 的 typedef。

【讨论】:

在某些操作系统上这两种情况都可以定义为unsigned long【参考方案8】:

size_t 是指针的大小。

所以在 32 位或常见的 ILP32(整数、长整数、指针)模型中 size_t 是 32 位。 并且在 64 位或常见的 LP64(长、指针)模型中 size_t 是 64 位(整数仍然是 32 位)。

还有其他模型,但这些是 g++ 使用的模型(至少在默认情况下)

【讨论】:

size_t 不一定与指针的大小相同,尽管通常如此。指针必须能够指向内存中的任何位置; size_t 只需大到足以表示最大单个对象的大小。 intptr_t 的大小可能与 void * 指针的大小相同。这不是必需的,但intptr_t 必须能够保存void * 指针的所有可能有效值。但是size_t 没有这个要求。还有size_t它至少16位,而指针可以更小。

以上是关于unsigned int 与 size_t的主要内容,如果未能解决你的问题,请参考以下文章

c语言中 int unsigned 类型转换

unsigned int 与 size_t

C语言中的unsigned int是啥

为啥 unsigned int 0xFFFFFFFF 等于 int -1?

unsigned short int 和 unsigned int 或 unsigned short 有啥区别?

unsigned char 与 char 有啥却别?何时适用