257

C 标准保证这size_t是一种可以保存任何数组索引的类型。这意味着,从逻辑上讲,size_t应该能够保存任何指针类型。我在谷歌上发现的一些网站上读到这是合法的和/或应该总是有效的:

void *v = malloc(10);
size_t s = (size_t) v;

所以在 C99 中,标准引入了intptr_tanduintptr_t类型,它们是有符号和无符号类型,保证能够保存指针:

uintptr_t p = (size_t) v;

size_t那么使用和 和有什么区别uintptr_t?两者都是无符号的,并且都应该能够保存任何指针类型,因此它们在功能上看起来是相同的。除了清晰之外,是否有任何真正令人信服的理由使用uintptr_t(或者更好的是 a void *)而不是 a size_t?在不透明的结构中,字段将仅由内部函数处理,有什么理由不这样做吗?

出于同样的原因,ptrdiff_t一直是一个有符号类型能够保存指针差异,因此能够保存大多数任何指针,那么它与 有何不同intptr_t

不是所有这些类型基本上都服务于相同功能的微不足道的不同版本吗?如果不是,为什么?我不能用其中一个做什么而我不能用另一个做什么?如果是这样,为什么 C99 会在语言中添加两种本质上是多余的类型?

我愿意忽略函数指针,因为它们不适用于当前的问题,但请随意提及它们,因为我偷偷怀疑它们将成为“正确”答案的核心。

4

7 回答 7

249

size_t是一种可以保存任何数组索引的类型。这意味着,从逻辑上讲, size_t 应该能够保存任何指针类型

不必要!回到分段 16 位架构的时代,例如:一个数组可能仅限于一个段(所以 16 位就可以)但是你可以有多个段(所以需要选择size_t一个 32 位类型intptr_t段以及其中的偏移量)。我知道在这些统一可寻址的未分段架构的时代,这些事情听起来很奇怪,但标准必须满足比“2009 年的正常情况”更广泛的变化,你知道的!-)

于 2009-09-23T06:05:46.460 回答
91

关于你的说法:

“C 标准保证这size_t是一种可以保存任何数组索引的类型。这意味着,从逻辑上讲,它size_t应该能够保存任何指针类型。”

这实际上是一个谬误(由不正确的推理导致的误解)(a)。您可能认为后者是从前者继承而来的,但事实并非如此。

指针和数组索引不是一回事。设想一个将数组限制为 65536 个元素但允许指针将任何值寻址到巨大的 128 位地址空间的一致实现是相当合理的。

size_tC99 声明变量的上限由定义,SIZE_MAX可以低至 65535(参见 C99 TR3, 7.18.3,在 C11 中未更改)。如果指针在现代系统中被限制在这个范围内,那么指针将是相当有限的。

在实践中,您可能会发现您的假设成立,但这并不是因为标准保证了这一点。因为它实际上并不能保证它。


(a)顺便说一句,这不是某种形式的人身攻击,只是说明为什么你的陈述在批判性思维的背景下是错误的。例如,下面的推理也是无效的:

所有的小狗都很可爱。这东西很可爱。所以这东西一定是小狗。

小狗的可爱与否在这里无关紧要,我只是说这两个事实并不能得出结论,因为前两句话允许存在不是小狗的可爱事物。

这类似于您的第一个声明,不一定要求第二个。

于 2009-09-23T06:08:34.527 回答
40

关于分段限制、奇异架构等的推理,我将让所有其他答案为自己辩护。

名称上的简单差异还不足以为正确的事物使用正确的类型吗?

如果要存储尺寸,请使用size_t. 如果要存储指针,请使用intptr_t. 阅读您的代码的人会立即知道“啊哈,这是一个大小,可能以字节为单位”,以及“哦,这是一个指针值,由于某种原因被存储为整数”。

否则,您可以对所有内容都使用unsigned long(或者,在这些现代时代,unsigned long long)。大小不是一切,类型名称具有有用的含义,因为它有助于描述程序。

于 2009-09-23T07:55:34.717 回答
12

最大数组的大小可能小于指针。考虑分段架构 - 指针可能是 32 位,但单个段可能只能寻址 64KB(例如旧的实模式 8086 架构)。

虽然这些在桌面机器中不再常用,但 C 标准旨在支持甚至小型的专用架构。例如,仍然有使用 8 位或 16 位 CPU 开发的嵌入式系统。

于 2009-09-23T06:05:05.877 回答
5

我会想象(这适用于所有类型名称)它可以更好地传达您在代码中的意图。

例如,即使unsigned shortwchar_t在 Windows 上大小相同(我认为),使用wchar_t而不是unsigned short表明您将使用它来存储宽字符的意图,而不仅仅是一些任意数字。

于 2009-09-23T06:07:18.233 回答
3

向后和向前看,并回想各种奇怪的架构散布在景观中,我很确定他们试图包装所有现有系统并提供所有可能的未来系统。

可以肯定的是,事情解决的方式,到目前为止,我们需要的类型并不多。

但即使在 LP64(一个相当常见的范例)中,我们也需要 size_t 和 ssize_t 作为系统调用接口。可以想象一个更受限制的遗留系统或未来系统,其中使用完整的 64 位类型是昂贵的,并且他们可能希望在大于 4GB 但仍具有 64 位指针的 I/O 操作上投入使用。

我想你必须想知道:可能已经开发了什么,未来可能会发生什么。(也许是 128 位分布式系统互联网范围的指针,但在系统调用中不超过 64 位,或者甚至可能是“遗留”的 32 位限制。:-) 遗留系统可能会获得新的 C 编译器的图像.. .

另外,看看当时存在的东西。除了数以万计的 286 个实模式内存模型,CDC 60 位字/18 位指针大型机又如何呢?克雷系列怎么样?没关系正常的 ILP64、LP64、LLP64。(我一直认为微软在 LLP64 上自命不凡,应该是 P64。)我当然可以想象一个委员会试图涵盖所有基础......

于 2009-09-23T06:14:18.377 回答
-11
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 ,反之亦然。

于 2011-02-04T11:45:04.840 回答