我们能否减去空指针?

7

由于指针算术是在同一数组中定义的,所以我怀疑我们是否可以将NULL从另一个NULL中减去。我担心以下实现:

//first and second can both either be from the same array 
//or be both NULL
prtdiff_t sub(void *first, void *second){
    //Do I really need this condition?
    if(!first && !second)
        return (ptrdiff_t) 0;

    return second - first;
}

注意:这个问题是关于C语言的。如果你在寻找关于C++的问题,可以在这里找到(答案不同!)。还有一个常见问题适用于C和C++。


4
@St.Antario,实际上您已经解释了它,并且可能已经有了您的参考资料... - Eugene Sh.
2
在这里检查 NULL 指针有一点帮助,因为任何 其他 无效指针同样会导致它出错。 - Eugene Sh.
2
@St.Antario,当我在寻找参考资料时,有两个答案被发布了。 :p - HarshitMadhav
1
@St.Antario 如果NULL被定义为整数常量表达式,那么你可以从NULL中减去NULL,但这不是可移植的,因为它可能没有(通常不会)被定义为整数常量表达式。 - Ian Abbott
2
即使NULL被定义为整数0,那么在赋值char *a = NULL; char *b = NULL;之后,减法a - b仍然是非法的。 - Ian Abbott
显示剩余8条评论
4个回答

11

不允许减去两个空指针。C标准的第6.5.6节第9段规定:

当两个指针相减时,它们都必须指向同一个数组对象或者指向该数组对象中的最后一个元素的下一个位置。结果是两个数组元素的下标之差。结果的大小是实现定义的,并且其类型(有符号整数类型)在头文件中已定义为ptrdiff_t。如果结果无法表示为该类型的对象,则行为是未定义的。换句话说,如果表达式P和Q分别指向数组对象的第i个和第j个元素,则表达式(P)-(Q)的值为i-j,前提是该值适合于ptrdiff_t类型的对象。此外,如果表达式P指向数组对象的一个元素或者该数组对象的最后一个元素之一,而表达式Q指向该数组对象的最后一个元素,则表达式((Q)+1)-(P)与((Q)-(P))+1和-((P)-((Q)+1))具有相同的值,如果表达式P指向数组对象的最后一个元素之一,则该值为零,即使表达式(Q)+1不指向数组对象的任何元素。因为没有一个指针指向一个数组对象,所以行为是未定义的。
你也不能减去两个void *类型的指针,因为void是一个不完整的类型,指针减法依赖于了解指向对象的大小。然而,你可以将每个指针转换为intptr_t类型,并进行减法运算,但这只会给你指针之间的字节差异,而不是索引差异。

2
在许多实现中,intptr_t 的差异将是一个字节差异,但将指针转换为整数的结果是由实现定义的,因此不能保证该结果。 - John Bollinger
@PaulOgilvie 我相信标准定义了NULL不是一个有效的地址(需要参考资料..)。在某些特定架构上,地址0实际上是有效的(并且将NULL表示为指向该地址的指针),这使得相应的架构/工具链组合不符合标准。 - Eugene Sh.
@EugeneSh.,很有趣。这意味着您不能编写用C语言编写的代码来修改位于地址0处的中断向量。 - Paul Ogilvie
7
@PaulOgilvie 这是引用:http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3 ..所得到的指针,称为空指针,保证与任何对象或函数的指针比较不相等。 但是,这可能是其中一个你只能说“好吧,我的编译器足够明智,能够理解我想做什么”的边角情况。 - Eugene Sh.
3
从语义上讲,“空指针值”仅保证与任何对象或函数的地址比较不相等。它不能保证是地址0(一些旧系统使用“0xDEADBEEF”作为空指针值)。它也被认为是一个“无效”的指针值(尝试对其进行解引用的行为是未定义的)。 - John Bode
显示剩余3条评论

7

不可以这么做:两个指针之间的差异仅在指向相同数组的元素或超出末尾的情况下定义。(对于此目的,一个对象被视为单个元素数组)。

但是,(intptr_t)second - (intptr_t)first 是有效的。


整数差异肯定是有效的,但是它的含义是实现定义的(如果没有溢出),因为将指针转换为整数的结果是实现定义的。 - John Bollinger
@JohnBollinger 实现定义中的 intptr_t 不需要被支持吗?还是以其他方式? - St.Antario
2
就像我之前所说的那样,@St.Antario,因为将指针值转换为整数类型的影响是由实现定义的,而与所涉及的特定整数类型无关,所以这是实现定义的。intptr_t是否可选是另一个问题。 - John Bollinger

0

C++03 §5.7/7 表示:

如果将值 0 添加到指针值或从指针值中减去,结果与原始指针值相等。如果两个指针指向同一对象或都指向同一数组的末尾或都为空,并且这两个指针被减去,则结果与转换为 ptrdiff_t 类型的值 0 相等。

但是 C 没有这样的规定。


所以C++可以处理你的情况。 - Ôrel
10
此问题标记为 C,而非 C++。 - dbush
2
如果你加上像“...但是C语言没有这样的规定”这样的内容和结论,你的答案可能会被认为是好的。 - Eugene Sh.
1
@dbus 我也在寻找关于C++的同样问题,但是谷歌默认只提供了这个(纯C)问题的链接。我跳过了这个答案,因为它被投票否决,并认为在C++中减去空指针也是无效的。但是这是错误的。这很误导人。这个答案正是我需要的。但是它被投票否决了。这很令人沮丧。无论如何,还有一个类似的C++问题 - anton_rh

-1

简单的答案是不行,你不能从另一个null中减去一个null。

我认为你误解了这个定义:

NULL明确定义为:具有值0的整数常量表达式,或将此类表达式强制转换为void类型的表达式称为空指针常量。所以我曾经认为我们可以从另一个0中减去一个0。

现在,让我们采用谷歌对NULL的定义

Null表示没有价值;换句话说,null就像零一样,就像你在咖啡里放了很少的糖,它几乎是零。 Null还表示无效。来自拉丁语nullus,意思是“没有任何东西”,可怜、无能的null实际上根本不存在。

显然,它说明null没有任何值。想象一下你试图从什么都没有中减去什么都没有

现在换句话说,null就是零(仅当定义了值时),你肯定可以减去它(但你不能像char *ab = NULL, char *aa= NULL这样做,然后执行像ab-aa这样的减法仍然是非法的)

但实际上没有人能够预测null的值,所以当你无法获取该值时,你就无法执行任何操作(如减法、加法等)。


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