C语言 使用 + 检查多个指针是否全部为 NULL



语法上是有道理的(虽然它看起来像其他语言,但我不是特别喜欢),它可以节省大量的打字和代码空间,但它有多糟糕?

if(p1 + (unsigned)p2 + (unsigned)p3 == NULL)
{
// all pointers are NULL, exit
}

使用带有指针 r 值的指针算术,我不明白它如何给出错误的结果(即使并非所有指针都是 NULL,也要计算为 NULL 的整个表达式),但我并不完全知道这可能隐藏了多少邪恶,所以这样做是不是不好的,检查大量指针是否都是 NULL 的不常见方法?

关于问题的原始版本,其中省略了演员表......

它可以节省大量的打字和代码空间,但它有多糟糕?

非常非常糟糕。 它的行为是完全未定义的,如果你的编译器无法拒绝它,那么你应该给自己一个更好的。 在某些情况下,会定义一个指针从另一个指针中减去(并产生整数结果),但添加两个指针永远不会有意义。

由于它甚至不应该编译,用于键入它的每次击键而不是工作的东西都被浪费了,所以不,它不会节省键入或代码空间。

我不明白它怎么会给出错误的结果。

如果编译器实际上接受它,则结果可以是任何内容。 它是未定义的

那么这样做是不是不好的,检查大量指针是否全部为 NULL 的不常见方法?

是的。


关于修改后的问题,其中除一个指针外的所有指针都转换为整数:

强制转换不会挽救代码 - 仍然存在多个问题。

  1. 如果剩余指针未指向有效对象,或者整数的总和为负数或大于指针指向的数组中的元素数,则指针添加的结果仍未定义(其中指向标量的指针被视为指向单元素数组的指针)。 当然,在这种特殊情况下,整数和不能是负数,但这具有很小的优势。

  2. C 不保证将空指针强制转换为整数会产生值 0。 它这样做是很常见的,但语言不需要它。

  3. C 不保证非空指针转换为非零整数,并且对于您的特定代码,这是一个真正的风险。 类型unsigned不一定大到足以为每个不同的指针提供不同的值。

  4. 即使上述所有内容对于某些特定实现都不是问题(也就是说,如果您可以安全地对 NULL 指针执行算术运算,并且 NULL 指针可靠地转换为零整数,并且非 NULL 指针可靠地转换为非零 - 测试仍然可能出错,因为两个非零无符号整数的总和可能为零。 当两者的算术总和等于UINT_MAX + 1.

这不是一种可靠的方法有多种原因。

首先,当您向指针添加整数时,C 标准不会说明如果结果在指针指向的数组之外会发生什么情况。(出于这些目的,仅指向最后一个元素(数组的末尾)的一个元素将计为内部,而不是外部。此外,指向单个对象的指针计为一个对象的数组。请注意,C 标准不仅没有说明添加的结果是什么;它没有说明整个程序的行为是什么。因此,一旦你执行了一个数组之外的加法,你就无法预测(从 C 标准)你的程序将做什么。

一个可能的结果是编译器将看到pointer + integer + integer和理由(或者,更技术地说,应用转换,就好像使用了这种推理一样)pointer + integer只有在pointerNULL时才有效,然后结果永远不会NULL,所以表达式pointer + integer永远不会NULL。同样,pointer + integer + integer永远不会NULL.因此pointer + integer + integer == NULL总是假的,我们可以通过完全删除此代码来优化程序。因此,当所有指针都NULL时,用于处理这种情况的代码将从程序中静默删除。

其次,即使 C 标准确实保证了加法的结果,即使没有任何指针NULL,这个表达式也可以假设计算为NULL。 例如,考虑一个 16 位地址空间,其中第一个指针用地址0x7000表示,第二个指针0x6000,第三个指针0x3000。(我也假设这些是char *指针,所以一个元素是一个字节。如果我们将这些相加,数学结果是0x10000。在 16 位算术中,这换行,因此计算结果是0x0000。因此,表达式的计算结果可能为零,这可能用于NULL

第三,unsigned可能比指针窄(例如,指针可能是 32 位,而指针是 64 位),因此强制转换可能会丢失信息 - 转换期间丢失的位中可能存在非零位,因此测试将无法检测到它们。

在某些情况下,我们想要优化指针测试,并且有合法但非标准的方法可以做到这一点。在某些处理器上,分支可能很昂贵,因此使用一个测试和一个分支进行一些算术可能比执行三个测试和三个分支更快。C 提供了一个用于处理指针表示的整数类型:uintptr_t,在<stdint.h>中声明。有了它,我们可以编写以下代码:

if (((uintptr_t) p1 | (uintptr_t) p2 | (uintptr_t) p3) == 0) …

这样做的作用是将每个指针转换为适合使用指针表示的宽度的无符号整数。C 标准没有说明这种转换的结果是什么,但它并不奇怪,平面地址空间的 C 实现可能会记录结果是内存地址。他们还可以记录NULL是零地址。一旦我们有了这些整数,我们就把它们放在一起,而不是将它们相加。如果设置了 OR 操作数中的任一相应位,则 OR 的结果将设置一个位。因此,如果任何一个地址不为零,则结果也不会为零。因此,如果在合适的 C 实现中执行此代码,将执行您想要的测试。

(我在特殊的高性能代码中使用了此类测试来测试所有指针是否按预期对齐,而不是测试NULL。在这种情况下,我可以直接访问编译器开发人员,并且可以确保编译器按预期运行。这不是标准的 C 代码。

在非数组指针上使用任何类型的指针算术在 C 中都是未定义的行为。

相关内容

  • 没有找到相关文章