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要在语言中添加两个本质上多余的类型?
我愿意忽略函数指针,因为它们不适用于当前的问题,但可以随意提及它们,因为我怀疑它们将是“正确”答案的核心。
关于你的声明:
C标准保证size_t是一种可以保存任何数组下标的类型。这意味着,从逻辑上讲,size_t应该能够保存任何指针类型。”
这实际上是一个谬论(由不正确的推理引起的误解)(a)。你可能认为后者源于前者,但事实并非如此。
指针和数组下标不是一回事。设想一个符合规范的实现,将数组限制在65536个元素,但允许指针将任何值寻址到一个巨大的128位地址空间,这是相当合理的。
C99声明size_t变量的上限由SIZE_MAX定义,它可以低至65535(参见C99 TR3, 7.18.3,在C11中不变)。在现代系统中,如果指针被限制在这个范围内,那么它们将受到相当的限制。
在实践中,您可能会发现您的假设成立,但这并不是因为标准保证了这一点。因为它并不能保证。
(a)顺便说一下,这不是某种形式的人身攻击,只是在批判性思维的背景下说明为什么你的陈述是错误的。例如,下面的推理也是无效的:
所有的小狗都很可爱。这东西很可爱。所以这东西一定是一只小狗。
小狗的可爱与否在这里没有任何关系,我只是说这两个事实并不能得出结论,因为前两句话允许存在可爱的东西,而不是小狗。
这类似于你的第一个陈述,但不一定要求第二个。
关于你的声明:
C标准保证size_t是一种可以保存任何数组下标的类型。这意味着,从逻辑上讲,size_t应该能够保存任何指针类型。”
这实际上是一个谬论(由不正确的推理引起的误解)(a)。你可能认为后者源于前者,但事实并非如此。
指针和数组下标不是一回事。设想一个符合规范的实现,将数组限制在65536个元素,但允许指针将任何值寻址到一个巨大的128位地址空间,这是相当合理的。
C99声明size_t变量的上限由SIZE_MAX定义,它可以低至65535(参见C99 TR3, 7.18.3,在C11中不变)。在现代系统中,如果指针被限制在这个范围内,那么它们将受到相当的限制。
在实践中,您可能会发现您的假设成立,但这并不是因为标准保证了这一点。因为它并不能保证。
(a)顺便说一下,这不是某种形式的人身攻击,只是在批判性思维的背景下说明为什么你的陈述是错误的。例如,下面的推理也是无效的:
所有的小狗都很可爱。这东西很可爱。所以这东西一定是一只小狗。
小狗的可爱与否在这里没有任何关系,我只是说这两个事实并不能得出结论,因为前两句话允许存在可爱的东西,而不是小狗。
这类似于你的第一个陈述,但不一定要求第二个。