C:两个void指针相减的类型是什么?

4
如果我减去两个空指针,我会得到它们之间的相对距离(至少在我的测试系统上是这样)。我应该使用哪种类型来存储结果?以便与64位系统兼容?size_t是正确的类型还是long?背景:我们需要检查给定的void指针是否安全用作double指针。我们曾经将指针强制转换为int并检查其低三位是否为零,但是我们当前的编码标准不再允许将指针转换为整数类型。我考虑计算空指针和NULL指针之间的差异,并检查该差异是否可被8整除。假设NULL指针始终是8字节对齐的?

太遗憾了,您不能将指针强制转换为uintptr_t,因为我们当前的编码标准不再允许这样做。 - chux - Reinstate Monica
4个回答

7

在C标准中,指针的void类型不支持相减运算。只有当两个指针都指向同一个数组时才能进行指针相减,而void指针无法指向任何数组,因为不能创建void元素的数组。

最有可能的情况是您正在使用gcc或类似的编译器,它允许将void指针视为char指针进行算术运算。但是,严格的C编译器不应该允许对void指针进行指针算术运算。


我们确实正在使用gcc。 - slingeraap
1
@slingeraap 然后将其配置为一个合适的 C 编译器:-std=c11 -pedantic-errors。当您尝试执行非标准操作时,您将会得到编译器错误提示。 - Lundin

4

C11, 6.5.6加法操作符/9(我的斜体):

当两个指针相减时,它们都必须指向同一数组对象的元素或该数组对象的最后一个元素之后的一个;结果是两个数组元素下标的差。结果的大小由实现定义,并且其类型(带符号整数类型)是在<stddef.h>头文件中定义的ptrdiff_t

根据这个部分,从指针中减去NULL也是不确定的行为,因为NULL无论如何都不能被视为指向同一数组的元素或其中一个元素后面的指针。指针减法真正的限制在于获取同一数组中两个元素之间的索引差异。

因此,您可能需要重新考虑您正在进行的检查,特别是考虑到双精度浮点数实际上并不需要是八个字节长的,即使是八个字节长,标准也没有要求它必须对齐在八字节边界上。

我更倾向于在您函数的文档中声明传递的值需要是有效的double指针,并且如果您函数的用户违反了这个约定,一切都将不确定。


在我们的系统上(基于PowerPC),双精度浮点数必须对齐到8字节边界。我知道这个检查本身并不可移植,但它仍然是必要的。 - slingeraap
@slingeraap 好奇:当指针不是“double”对齐时,你的代码会做什么? - chux - Reinstate Monica

4
如果我减去两个void指针,我会得到指针位置之间的相对距离(至少在我的测试系统上是这样)。有一个严格的规则来减去两个指针。C11-§6.5.6:
当两个指针被减去时,它们都必须指向同一数组对象的元素或该数组对象的最后一个元素;结果是两个数组元素下标的差值。 [...]
两个void指针之间的差应该是ptrdiff_t类型。
[...] 结果的大小是实现定义的,其类型(有符号整数类型)由头文件中定义的ptrdiff_t类型定义。 [...]

4

问题概述:

  • 您混淆了空指针常量宏NULL和空指针。这是两个不同的术语。NULL宏始终等同于0,但空指针可以具有任何值。请参考此处

  • 话虽如此,使用空指针或NULL宏进行指针算术运算毫无意义。这样做会引发未定义行为,因为指针必须指向相同的数组。请参见paxdiablo的答案。

  • 此外,正如Art的答案中所述,您不能对void指针进行任何形式的算术运算。+和-运算符的规定如下(6.5.6):

    对于加法,要么两个操作数都必须具有算术类型,要么一个操作数必须是指向完整对象类型的指针,另一个操作数必须具有整数类型。

    void指针是指向不完整类型的指针,因此如果其中一个或两个操作数是void指针,则您的代码无效。

    请注意,GCC具有一种非标准扩展,允许对void指针进行算术运算,通过将void指针视为指向字节的指针来处理这种情况。如果您打算编写安全且可移植的程序,请勿使用此非标准功能。如果您希望GCC表现为符合规范的C编译器(符合实现),请使用gcc -std=c11 -pedantic-errors进行编译。


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