从语法上讲它是有意义的(尽管它看起来像其他语言,我并不特别喜欢),它可以节省大量的打字和代码空间,但它有多糟糕?
if(p1 + (unsigned)p2 + (unsigned)p3 == NULL)
{
// all pointers are NULL, exit
}
使用带有指针右值的指针算法,我不知道它是如何产生错误结果的(即使并非所有指针都是NULL,整个表达式也要求为NULL),但我并不完全相同知道这可能隐藏了多少邪恶,这样做是不是很糟糕,不常见的方法是检查大量指针是否都是NULL?
答案 0 :(得分:5)
关于问题的原始版本,省略了演员......
它可以节省大量的输入和代码空间,但它有多糟糕?
非常非常糟糕。它的行为完全未定义,如果你的编译器没有拒绝它,那么你应该让自己变得更好。在某些情况下定义一个指针从另一个指针的减法(并产生一个整数结果),但添加两个指针永远不会有意义。
因为它甚至不应该编译,所以用来键入它的每个键击而不是有用的键都是浪费,所以不,它不会节省输入或代码空间。
我不知道它是如何产生错误结果的。
如果编译器实际接受它,结果可以是任何东西。它是未定义。
这样做不好,不常见的方法是检查是否有大量指针都是NULL?
是
关于修改后的问题,其中除了一个指针之外的所有指针都转换为整数:
演员不会拯救代码 - 仍存在多个问题。
如果剩余指针未指向有效对象,或者整数之和为负或大于指针所指向的数组中的元素数,则指针添加的结果为仍未定义(将指向标量的指针视为指向单元素数组的指针)。当然,在这种特殊情况下,整数和不能为负,但这是最小的优势。
C不保证向整数投射空指针会产生值0.通常它会这样做,但语言不需要它。
C不保证非空指针转换为非零整数,并且您的特定代码具有真正的风险。类型unsigned
不一定足够大,不能为每个不同的指针提供明确的值。
即使所有上述内容都不是某些特定实现的问题 - 也就是说,如果您可以安全地对NULL指针执行算术运算,并且NULL指针可靠地转换为整数为零,并且非NULL可靠地转换为非零的指针 - 测试可能仍然出错,因为两个非零无符号整数可以总和为零。这发生在两者的算术和等于UINT_MAX + 1
。
答案 1 :(得分:3)
这不是一种可靠的方法有多种原因。
首先,当您向指针添加整数时,C标准不会说明如果结果位于指针所指向的数组之外会发生什么。 (出于这些目的,仅指向最后一个元素的一个,即数组的结尾,计为内部,而不是外部。另外,指向单个对象的指针计为一个对象的数组。)请注意,C标准不是只是不说加法的结果是什么;它没有说明整个计划的行为是什么。因此,一旦执行了超出数组的添加,就无法预测(根据C标准)您的程序将执行什么操作。
一个可能的结果是,编译器将看到pointer + integer + integer
和原因(或者,更技术上,应用转换,就像使用了这种推理一样)pointer + integer
仅在pointer
是有效时才有效不是NULL
,然后结果永远不会是NULL
,因此表达式pointer + integer
永远不会是NULL
。同样,pointer + integer + integer
永远不会NULL
。因此pointer + integer + integer == NULL
始终为false,我们可以通过完全删除此代码来优化程序。因此,当所有指针都为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的结果具有位设置。因此,如果任何一个地址不为零,那么结果也不会为零。因此,如果在适当的C实现中执行,此代码将执行您想要的测试。
(我在特殊的高性能代码中使用过这样的测试来测试所有指针是否按照需要对齐,而不是测试NULL
。在这种情况下,我可以直接访问编译器开发人员并且可以确保编译器的行为符合要求。这是不标准C代码。)
答案 2 :(得分:0)
对非数组指针使用任何类型的指针算法都是C中未定义的行为。