size_t vs. uintptr_t

时间:2009-09-23 05:59:42

标签: c pointers size-t

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

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

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

uintptr_t p = (size_t) v;

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

出于同样的原因,ptrdiff_t是一个能够保持指针差异的签名类型,因此能够容纳大多数指针,那么它与intptr_t的区别是什么?

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

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

7 个答案:

答案 0 :(得分:223)

  

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

不一定!回到分段16位架构的时代,例如:一个数组可能仅限于一个段(因此16位size_t会这样做)但是你可以有多个段(所以一个32位{选择段以及其中的偏移量需要{1}}类型。我知道这些事情听起来很奇怪,这些日子是统一可寻址的未分段架构,但标准必须提供比“2009年的正常情况”更广泛的品种,你知道! - )

答案 1 :(得分:85)

关于你的陈述:

  

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

这实际上是一种谬误(由于错误推理导致的误解)(a)。你可以认为后者来自前者,但事实并非如此。

指针和数组索引是一回事。设想一个符合条件的实现将数组限制为65536个元素,但允许指针将任何值解析为一个巨大的128位地址空间,这是非常合理的。

C99指出size_t变量的上限由SIZE_MAX定义,这可以低至65535(参见C99 TR3,7.18.3,在C11中保持不变)。如果在现代系统中将指针限制在这个范围内,则指针会相当有限。

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


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

  

所有的小狗都很可爱。这件事很可爱。因此,这件事必须是一只小狗。

puppiess的可爱或其他方面在这里没有任何影响,我所说的只是两个事实并不能得出结论,因为前两个句子允许存在 not < / em>小狗。

这类似于你的第一句话,不一定要求第二句。

答案 2 :(得分:33)

我会让所有其他答案代表他们自己对段限制,异国情调架构等的推理。

名称​​中的简单差异是否足以使用正确的类型才能正确使用?

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

否则,您可以使用unsigned long(或者,在这些现代,unsigned long long)来处理所有事情。大小不是一切,类型名称带有有用的含义,因为它有助于描述程序。

答案 3 :(得分:12)

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

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

答案 4 :(得分:5)

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

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

答案 5 :(得分:3)

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

当然,事情已经解决了,我们到目前为止还没有那么多类型。

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

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

另外,看看周围存在的东西。除了zillion 286实模式内存模型外,CDC 60位字/ 18位指针主机怎么样? Cray系列怎么样?别介意普通的ILP64,LP64,LLP64。 (我一直认为微软对LLP64很有说服力,应该是P64。)我当然可以想象一个委员会试图覆盖所有基地...

答案 6 :(得分:-9)

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,反之亦然。