我们可以减去 NULL 指针吗?

Posted

技术标签:

【中文标题】我们可以减去 NULL 指针吗?【英文标题】:Can we subtract NULL pointers? 【发布时间】:2019-09-08 21:16:22 【问题描述】:

由于指针运算是在同一个数组中定义的,我怀疑我们是否可以从另一个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;

【问题讨论】:

@St.Antario 实际上你已经自己解释过了,可能有你的参考... 好吧,在这里检查 NULL 会有一点帮助,因为任何 other 无效指针都会同样破坏它。 @St.Antario 在我寻找参考资料时发布了两个答案。 :p @St.Antario 所以如果NULL 被定义为一个整数常量表达式,你可以从NULL 中减去NULL,但这并不是不可移植的,因为它可能不是(而且通常是't) 定义为整数常量表达式。 即使NULL被定义为整数0,那么在赋值char *a = NULL; char *b = NULL;之后,那么减法a - b仍然是非法的。 【参考方案1】:

不允许减去两个 NULL 指针。 C standard states的第6.5.6p9节:

当两个指针相减时,都指向 相同的数组对象,或数组的最后一个元素 目的;结果是下标的差异 两个数组元素。结果的大小是 实现定义,其类型(有符号整数类型)为 ptrdiff_t 在标头中定义。如果结果不是 可以在该类型的对象中表示,行为是 不明确的。换句话说,如果表达式 P 和 Q 指向, 分别地,我 -th 和 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 并减去它们,但这会给您指针之间的字节差异,而不是索引差异。

【讨论】:

在许多实现中 intptr_t 的差异将是字节差异,但是将指针转换为整数的结果是实现定义的,因此不能保证结果. 技术上 NULL 是一个有效的地址并且是可寻址内存的第一个字节。因此它可以指向一个数组对象,因此您可以从 NULL 中减去 NULL。只有内存管理硬件被告知对于应用程序 NULL 不是一个有效的地址,并且在取消引用时它应该抛出一个异常。示例:中断向量从零开始。 @PaulOgilvie 我相信NULL 被定义为不是标准的有效地址(需要参考..)。地址0 实际有效(并且NULL 表示与指向此地址的指针相同)的特定架构的情况使得相应的架构/工具链组合不兼容。 @PaulOgilvie 引用如下:port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3 ..生成的指针,称为空指针,保证不等于指向任何对象或函数的指针。 .但是,是的,这将是这些极端情况之一,您只需说“好吧,我的编译器足够理智,可以理解我想要做什么”.. @PaulOgilvie:从语义上讲,空指针值只能保证不等于任何对象或函数的地址。它不能保证是地址 0(一些旧系统使用 0xDEADBEEF 作为空指针值)。它也被认为是一个 invalid 指针值(试图取消引用它的行为是未定义的)。【参考方案2】:

不,您不能这样做:两个指针之间的差异仅针对指向同一数组元素的指针定义,或者指向末尾的元素。 (为此,一个对象算作一个元素数组)。

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

【讨论】:

整数差异肯定是有效的,但它的含义是实现定义的(前提是没有溢出),因为将指针转换为整数的结果是实现定义的. @JohnBollinger 在intptr_t 的意义上实现定义的不需要支持?还是以其他方式? 正如我所说,@St.Antario,实现定义,因为将指针值转换为整数类型的效果是实现定义的,无论涉及的特定整数类型如何。 intptr_t 是可选的,这是一个单独的问题。【参考方案3】:

简单的答案是NO, YOU CAN'T SUBTRACT A NULL FROM ANOTHER NULL.

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

NULL 明确定义为:一个整数常量表达式 值 0 或这种类型转换为 void 的表达式称为 null 指针常量。所以我曾经认为我们可以减去一个 0 另一个 0.

现在,至于现在,让我们来看看 GOOGLE 对 NULL 的定义

Null 表示没有价值;换句话说,null 为零,就像你 在你的咖啡里放这么少的糖,它实际上是无效的。空值 也意味着无效。源自拉丁语 nullus, 意思是“不是任何”,可怜的、无能为力的 null 实际上根本不存在。

很明显,它表明 null 没有 没有价值。想象一下,您正试图从无中减去任何内容

现在让我们换一种说法,null 为零(当且仅当定义值)你可以肯定地减去它(但你不能做类似 char *ab = NULL, char *aa= NULL 然后执行像 ab-aa 这样的减法仍然是非法的)

但没有人能真正预测 null 的值,因此,当您无法获得该值时,您无法对其执行任何操作(如减法、加法等)。

【讨论】:

【参考方案4】:

C++03 §5.7/7 说:

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

但是C没有这样的规定。

【讨论】:

所以 C++ 处理你的情况。 这个问题被标记为 C,而不是 C++。 如果您添加类似“..但 C 没有这样的规定”之类的内容和结论,您的答案可能会被认为是好的答案。

以上是关于我们可以减去 NULL 指针吗?的主要内容,如果未能解决你的问题,请参考以下文章

对 NULL 指针连续 free多次会出错吗?为啥?

释放指针后真的应该将指针设置为“NULL”吗?

我们如何检查指针是不是为 NULL 指针?

在 C 中释放 NULL 指针是一种好习惯吗? [复制]

字符指针用free释放后,需要设置null吗?

指针free后还存在吗?