C 标准保证 size_t
是可以保存任何数组索引的类型。这意味着,从逻辑上讲,size_t
应该能够保存任何指针类型。我在谷歌上发现的一些网站上读到这是合法的和/或应该总是有效的:
void *v = malloc(10);
size_t s = (size_t) v;
因此在 C99 中,标准引入了 intptr_t
和 uintptr_t
类型,它们是保证能够保存指针的有符号和无符号类型:
uintptr_t p = (size_t) v;
那么使用 size_t
和 uintptr_t
有什么区别?两者都是无符号的,并且都应该能够保存任何指针类型,因此它们在功能上看起来是相同的。除了清晰之外,是否有任何真正令人信服的理由使用 uintptr_t
(或者更好的是 void *
)而不是 size_t
?在不透明结构中,字段仅由内部函数处理,有什么理由不这样做吗?
同样的道理,ptrdiff_t
一直是一个有符号类型,能够保存指针差异,因此能够保存大多数指针,那么它与 intptr_t
有什么区别呢?
不是所有这些类型基本上都服务于相同功能的微不足道的不同版本吗?如果不是,为什么?我不能用其中一个做什么而我不能用另一个做什么?如果是这样,为什么 C99 会在语言中添加两种本质上是多余的类型?
我愿意忽略函数指针,因为它们不适用于当前的问题,但可以随意提及它们,因为我偷偷怀疑它们将成为“正确”答案的核心。
size_t 是一种可以保存任何数组索引的类型。这意味着,从逻辑上讲, size_t 应该能够保存任何指针类型
不必要!回到分段 16 位架构的时代,例如:一个数组可能仅限于一个段(所以 16 位 size_t
可以)但是你可以有多个段(所以一个 32 位 intptr_t
type 需要选择段以及其中的偏移量)。我知道在这些统一可寻址的未分段架构的时代,这些事情听起来很奇怪,但标准必须满足比“2009 年的正常情况”更广泛的变化,你知道的!-)
关于你的说法:
“C 标准保证 size_t 是一种可以保存任何数组索引的类型。这意味着,从逻辑上讲,size_t 应该能够保存任何指针类型。”
这实际上是一个谬误(由不正确的推理导致的误解)(a)。您可能认为后者是从前者继承而来的,但事实并非如此。
指针和数组索引不是一回事。设想一个将数组限制为 65536 个元素但允许指针将任何值寻址到巨大的 128 位地址空间的一致实现是相当合理的。
C99 声明 size_t
变量的上限由 SIZE_MAX
定义,可以低至 65535(参见 C99 TR3, 7.18.3,在 C11 中未更改)。如果指针在现代系统中被限制在这个范围内,那么指针将是相当有限的。
在实践中,您可能会发现您的假设成立,但这并不是因为标准保证了这一点。因为它实际上并不能保证它。
(a) 顺便说一句,这不是某种形式的人身攻击,只是说明为什么你的陈述在批判性思维的背景下是错误的。例如,下面的推理也是无效的:
所有的小狗都很可爱。这东西很可爱。所以这东西一定是小狗。
小狗的可爱与否在这里无关紧要,我只是说这两个事实并不能得出结论,因为前两句话允许存在不是小狗的可爱事物。
这类似于您的第一个声明,不一定要求第二个。
ptrdiff_t
与 intptr_t
部分)。
关于分段限制、奇异架构等的推理,我将让所有其他答案为自己辩护。
名称上的简单差异还不足以为正确的事物使用正确的类型吗?
如果要存储大小,请使用 size_t
。如果要存储指针,请使用 intptr_t
。阅读您的代码的人会立即知道“啊哈,这是某物的大小,可能以字节为单位”,以及“哦,这是一个指针值,由于某种原因被存储为整数”。
否则,您可以只使用 unsigned long
(或者,在现代,unsigned long long
)来处理所有事情。大小不是一切,类型名称具有有用的含义,因为它有助于描述程序。
size_t
字段中。
void*
、intptr_t
和 uintptr_t
保证能够表示任何指向数据的指针。
最大数组的大小可能小于指针。考虑分段架构 - 指针可能是 32 位,但单个段可能只能寻址 64KB(例如旧的实模式 8086 架构)。
虽然这些在桌面机器中不再常用,但 C 标准旨在支持甚至小型的专用架构。例如,仍然有使用 8 位或 16 位 CPU 开发的嵌入式系统。
size_t
也应该能够处理吗?或者,某个遥远段中的动态数组是否仍仅限于在其段内进行索引?
str
函数,Borland 甚至用于 mem
函数(memset
、memcpy
、memmove
)。这意味着您可以在偏移溢出时覆盖部分内存,这在我们的嵌入式平台上调试很有趣。
我会想象(这适用于所有类型名称)它可以更好地传达您在代码中的意图。
例如,即使在 Windows 上 unsigned short
和 wchar_t
大小相同(我认为),使用 wchar_t
而不是 unsigned short
表明您将使用它来存储宽字符,而不仅仅是一些任意数。
wchar_t
比 unsigned short
大得多,因此将一个用于另一个将是错误的,并且会产生严重的(和现代的)可移植性问题,而 size_t
和uintptr_t
似乎位于 1980 年左右的遥远土地上(日期在黑暗中随机刺伤,那里)
size_t
和 uintptr_t
在它们的名字中仍然有隐含的用途。
向后和向前看,并回想各种奇怪的架构散布在景观中,我很确定他们试图包装所有现有系统并提供所有可能的未来系统。
可以肯定的是,事情解决的方式,到目前为止,我们需要的类型并不多。
但即使在 LP64(一个相当常见的范例)中,我们也需要 size_t 和 ssize_t 作为系统调用接口。可以想象一个更受限制的遗留系统或未来系统,其中使用完整的 64 位类型是昂贵的,并且他们可能希望在大于 4GB 但仍具有 64 位指针的 I/O 操作上投入使用。
我想你必须想知道:可能已经开发了什么,未来可能会发生什么。 (也许是 128 位分布式系统互联网范围的指针,但在系统调用中不超过 64 位,或者甚至可能是“遗留”的 32 位限制。:-) 遗留系统可能会获得新的 C 编译器的图像.. .
另外,看看当时存在的东西。除了数以万计的 286 个实模式内存模型,CDC 60 位字/18 位指针大型机又如何呢?克雷系列怎么样?没关系正常的 ILP64、LP64、LLP64。 (我一直认为微软在 LLP64 上自命不凡,应该是 P64。)我当然可以想象一个委员会试图涵盖所有基础......
int main(){
int a[4]={0,1,5,3};
int a0 = a[0];
int a1 = *(a+1);
int a2 = *(2+a);
int a3 = 3[a];
return a2;
}
暗示 intptr_t 必须始终替换 size_t ,反之亦然。
size_t
和uintptr_t
之间的区别,但是ptrdiff_t
和intptr_t
呢?它们不能存储相同的范围几乎所有平台上的价值?为什么有有符号和无符号指针大小的整数类型,特别是如果ptrdiff_t
已经用于有符号指针大小的整数类型的目的。size_t
至少为 16 位,但ptrdiff_t
至少为 17 位(这实际上意味着它可能至少为 32 位)。SIZE_MAX
不应该是 2**64。请注意,这是使用平面寻址;为了使SIZE_MAX
和数据指针的范围不匹配,不需要分段。