为什么ptrdiff_t不同于long long?

3
根据当前的C标准,指针之间的差异类型为ptrdiff_t。同时,根据标准,数组下标可以是任何整数类型,最大的有符号整数类型目前是long long
如果ptrdiff_t不是long long,而是更小的话,那么上述两个语句就会产生矛盾,不是吗?
那么为什么标准引入了ptrdiff_t类型,而不只是使用long long作为指针差异呢?

3
unsigned long long 可作为数组下标并不代表所有可能的 unsigned long long 值都是有效的数组下标值。(另外,指针之间的差值是带符号的。) - user2357112
你能详细说明一下你的第一句话吗?我完全不明白。在我看来,恕我无知,这两件事是相同的。 - Mark Galeck
1
如果下标的值超出了 size_t 的范围,则结果是未定义的。数组的下标可以是任何整数类型。 - chux - Reinstate Monica
@chux 在哪里写到了当前的标准呢?我没有看到。 - Mark Galeck
1
@MarkGaleck size_t 足够大,可以表示任何对象的大小,因此它的范围提供了所有对象大小的上界。 - melpomene
显示剩余3条评论
3个回答

6
首先,ptrdiff_t是有符号类型(两个指针之间的差值可以为负数),因此排除了unsigned long long
数组下标可以是任何整数类型,但结果值必须在边界内。例如,在32位平台上,您不能拥有超过约40亿个元素的数组,因此ptrdiff_t可能是32位整数类型。使用超过32位的long long值对数组进行索引只会导致未定义的行为(越界访问)。
通常,真正的原因是向后兼容性:long long是在ptrdiff_t之后很长时间才添加到语言中的,而ptrdiff_t是在编译器已经编写之后才添加的。当存在依赖于(几十年前的)行为的现有代码时,指针算术的完全不同规则(例如,“减去两个指针必须产生64位以上的结果”)是没有意义的。

谢谢。是的,我认为对我来说理解重要的事情是,一旦数组已经成功声明或分配,那么根据定义,元素数量必须足够小,可能小到必须适合比long long更小的整数类型中。 - Mark Galeck
1
数组下标...结果值必须在size_t的范围内。即使在同一数组中,ptrdiff_t并不一定足够宽以容纳所有指针差异。C11 §6.5.6 9规定:“如果结果无法表示为该类型的对象,则行为未定义。”然而,对于大多数应用程序来说,ptrdiff_t已经足够了。 - chux - Reinstate Monica

0

我想补充之前的回答,指针的大小可能比数据寄存器的大小小或大,这取决于机器架构。

此外,物理内存大小可能比地址空间大。

因此,可能的架构种类很多,所有这些都必须被覆盖。因此,即使ptrdiff_t是某个具体有符号整数类型的别名,我认为保持与指针相关的类型独立于数据整数类型的定义是完全合理的。

我总是尽可能地使我的代码可移植,并且为了实现这一点,我尽可能严格地遵守标准并使用最适合我的需求的最具体的标准类型。


0

你错了。 ptrdiff_t 可以是 long long int 的别名。这取决于所使用的系统和地址寄存器的大小。

因此,数组的大小不能超过可以存储在地址寄存器中的值。

还有另一个问题可能导致未定义行为。

问题在于两个指针之间的差值可能是负数。因此,你不能将无符号数用作结果。此外,索引也可能是负数。例如,请考虑以下代码片段:

const char *s = "aabbcc";
size_t n = 0;

for ( const char *p = s; *p++; )
{
    if ( p[0] == p[-1] ) ++n;
}

这是有效的代码。

因此,指针算术需要有符号数。

另一方面,对象的大小由类型 size_t 决定,它可以与类型 unsigned long long 一样大。

问题在于,任何有符号整数类型的非负值范围都不大于具有相同等级的无符号类型的非负值范围。而存储在寄存器中的绝对地址是非负数。


是的,我的问题关于标志不够精确,抱歉...但实际上,问题并不在于此。谢谢。 - Mark Galeck

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