为什么数组大小可以用 int 表示,而使用 size_t 呢?

15
C标准保证int类型能够存储所有可能的数组大小,至少这是我从阅读§6.5.2.1,子节1(数组下标约束)中了解到的。 引用语句如下: “其中一个表达式应具有“指向对象类型”的指针类型,另一个表达式应具有整数类型,并且结果具有“类型”类型。” 由于我们将使用int作为数组下标,那么为什么我们应该使用size_t来确定数组的大小呢? 为什么strlen()返回size_t,而使用int也可以?

3
size_t通常是无符号的。由于字符串大小为-1似乎有些毫无意义,所以我为什么要支持它呢? - forsvarir
不是通常的情况。它需要是无符号类型。一些有缺陷的实现曾经使用带符号的 size_t,这导致了极其严重的可利用漏洞。 - R.. GitHub STOP HELPING ICE
1
还有一个malloc,它以size_t作为参数。而且很难知道该使用什么作为数组下标:https://dev59.com/x3A75IYBdhLWcg3wqK7_ - Ciro Santilli OurBigBook.com
5个回答

26

“整数类型”这个术语并不是指 int - 例如,charshort 都是整数类型。

仅仅因为你可以使用一个 int 来对数组进行下标操作,并不意味着它能够到达所有可能的数组元素。

关于 size_tint 的更具体区别,一个例子就是在某些平台上,int 可能是一个16位类型,而 size_t 可能是一个32位类型(或者是今天的64位平台上相较更为普遍的32位 int vs 64位 size_t 差异)。


6
整数类型不一定是“int”。像“long long”和“size_t”也是整数类型。
数组可以大于2GB。这对那些编写内存需求量大的程序很有用,例如具有大缓冲池的DBMS、具有大内存缓存的应用服务器等。大于2GB/4GB的数组是64位计算机的全部意义 :)
对于strlen(),size_t至少听起来与C标准处理数组相兼容,无论是否实际上可行,或者是否有人见过如此大的字符串,都是另一个问题。

1
虽然理论上它们可以存在,但最好的做法是禁止程序使用超过 INT_MAX 大小的字符串,因为 snprintf 返回 int 并在结果字符串太长时给出溢出错误(即如果您想允许超过 2GB 的字符串,则最佳安全处理字符串的方式变得无法使用)。 - R.. GitHub STOP HELPING ICE

2
首先,你引用的标准并没有特别指出类型 int。同时,在 C 语言中,并不保证 int 可以足够存储任何对象(包括数组)的大小。
其次,C 语言并没有特别的 "数组下标"。数组下标是通过指针运算来实现的。指针运算中的整型操作数类型为 ptrdiff_t,而不是 size_t 或者 int。需要注意的是,ptrdiff_t 是带符号类型,这意味着它的值可以是负数。
第三,size_t 的作用是存储程序中任何对象的大小(即存储 sizeof 的结果)。它并不是专门用于数组索引。之所以可以用作数组索引,仅仅是因为它保证足够大以对任何数组进行索引。然而,从抽象的角度来看,"数组" 是一种特定的 "容器",还有其他种类的容器存在(如基于链表或基于树结构的)。在一般情况下,size_t 并不足以存储 任何 容器的大小,因此在一般情况下,将它用作数组索引并不是一个理想的选择。(另一方面,strlen 是一个专门用于处理数组的函数,因此在那里使用 size_t 是适当的。)

2
鉴于callocqsortfread/fwrite都使用一对size_t值来表示元素大小和元素数量,我不同意您的说法,即size_t不适用于数组索引。此外,指针算术中的整数操作数可以是任何整数类型,而不仅仅是ptrdiff_t,但是指针相减的结果具有ptrdiff_t类型。 - R.. GitHub STOP HELPING ICE
2
@Vladislav Vaintroub:不行。根据定义,size_t 只能保存您的实现允许创建的最大连续对象的大小。这就是它所能做的全部。没有任何保证您可以创建占用整个内存的对象,因此不能保证 size_t 足以索引整个内存。可用于索引整个内存的整数类型称为 uintptr_t,而不是 size_tsize_t 在一般情况下比“整个内存”要小。 - AnT stands with Russia
3
分段内存平台通常具有“小”的size_t,例如DOS / Win16或IBM 128位平台。这些是我的“反例”。但我不关心任何“反例”。反例无关紧要。重要的是size_t实现了一个非常具体的概念。而这个概念与通用计数无关。这立即使size_t不适用于通用计数。它是否“足够”都没有任何关系。 - AnT stands with Russia
2
6.5.6/9:指针减法的说法是,“如果结果在[ptrdiff_t]中无法表示,则行为未定义”。由此可以清楚地看出,ptrdiff_t不能保证足够大以包含对象中的任何偏移量。而且,ptrdiff_t不是指针算术中的“整数操作数”。任何整数类型都可以用于指针加法,标准只将ptrdiff_t作为减法的特例。size_t是定义为可用于对象中任何正偏移量的类型,而不是ptrdiff_t。它并不是“碰巧”有效,而是被定义为这样。offsetof也使用它。 - Steve Jessop
2
@AndreyT:对象大小和元素数量并不是完全无关的概念。数组的大小以元素为单位等于其元素数量。数组的大小以字节为单位等于其元素数量乘以每个元素的字节数。这就是数组。也许有一些更高级的抽象,你想将数组视为特殊情况,但C语言没有这样做,所以你的偏好与C几乎没有关系,当然不能证明每个人都停止使用size_t来执行它设计的功能。在其他语言中,也许可以。 - Steve Jessop
显示剩余16条评论

0

在编写C标准时,机器通常具有16位的“int”类型,并且无法处理大于65535字节的任何单个对象,但仍然能够处理大于32767字节的对象。由于无符号int上的算术运算足以处理这些对象的最大大小,但有符号int上的算术运算则不行,因此定义了size_t为无符号,以便容纳这些对象而无需使用“long”计算。

在允许的最大对象大小介于INT_MAX和UINT_MAX之间的机器上,指向该对象开头和结尾的指针之间的差异可能太大而无法适应“int”。虽然标准没有强制要求实现应如何处理它,但常见的方法是定义整数和指针环绕行为,使得如果S和E是char [49152]的开始和结束的指针,则即使E-S超过INT_MAX,它将产生一个值,当添加到S时,将产生E。

现在,size_t是一个无符号类型的事实很少有真正的优势(因为需要大于2GB的对象的代码通常需要出于其他原因使用64位指针),并且它会导致许多涉及对象大小的比较行为反直觉,但sizeof表达式产生无符号类型的事实已经深入人心,不太可能改变。


-3

size_t 是无符号整数的 typedef(如 int 或 long)。

在一些 64 位平台上,int 可能是 32 位,而 size_t 可能是 64 位。

它被用作更标准的尺寸表示方式。


"size_t是unsigned int的typedef,但并非总是如此。" - Blagovest Buyukliev
不,size_t是某个无符号类型的typedef。隐藏在size_t背后的类型甚至可能是非标准的,即它可能无法表示为unsigned intunsigned long等。 - AnT stands with Russia
2
理论上,size_t 可能是 unsigned long long 或者甚至是 uintmax_t - Jonathan Leffler

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