首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在最大基础整数值附近使用哨兵指针值安全吗?

在最大基础整数值附近使用哨兵指针值安全吗?
EN

Stack Overflow用户
提问于 2019-04-10 14:53:48
回答 1查看 191关注 0票数 6

我正在研究一些代码,除了空指针之外,还使用一些特殊的值,比如(T*)-1,如果某些“创建”函数失败,通常作为返回值。

如果所指向的类型是一个足够大的类型,以至于((T*)-n) + sizeof(T)会溢出,这意味着该地址永远无法为类型T的实例实际分配,这可以吗?编译器能看到像if (ptr == (T*)-1)这样的东西,判断这是不可能的,并对其进行优化吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-10 17:59:04

TL;DR(T*)-1可能在实践中按预期工作,但是为了安全、可移植和将来的防伪,您应该使用空指针作为哨兵。

我正在研究一些代码,除了空指针之外,还使用一些特殊的值,比如(T*)-1,如果某些“创建”函数失败,通常作为返回值。

实际上,一些POSIX接口(如shmat() )的行为类似,返回(void *)-1以指示错误。对于它们来说,这相当于返回int值-1的许多其他标准函数。它是一个永远不会成为成功调用的有效返回值的值。因此,这必须适用于每个符合POSIX的实现,而且我认为其他POSIX需求的综合效果是,对于void *以外的指针类型,也需要使用相同的指针类型。

更普遍的情况是,C显式地允许整数不受限制地转换为指针,但请注意

除了空指针常量外,结果是实现定义的,可能没有正确对齐,可能不会指向引用类型的实体,而且可能是陷阱表示。

(C2011,6.3.2.3/5)。因此,与这种转换有关的主要问题是

  • (T*)-1的结果是陷阱表示,在这种情况下,您描述的方案会产生未定义的行为。
  • (T*)-1的结果可能是指向T的有效指针,在这种情况下,使用它作为哨兵是不安全的。

据我所知,对于您可能遇到的任何C实现来说,第一个都不是问题。我认为第二个问题在实践中也不太可能成为你的问题,但如果你的目标是非POSIX系统,那么我对此就不那么自信了。

你接着问,

如果所指向的类型是一个足够大的类型,以至于(( T *)-n) +sizeof of (T)将溢出,这意味着该地址永远无法为类型T的实例实际分配,这可以吗?编译器能看到像if (ptr == (T*)-1)这样的东西,判断这是不可能的并对其进行优化吗?

这是一个有趣的问题。假设(T*)-1不产生陷阱表示,本条款适用于:

两个指针比较相等的当且仅当都是空指针,它们都是指向同一个对象的指针(包括指向对象和子对象的指针)或函数,它们都是指向同一个数组对象的最后一个元素的指针,或者一个指针指向一个数组对象结束后的一个指针,另一个指针指向一个不同数组对象的开始,该指针恰好紧跟在地址空间中的第一个数组对象后面。

(C2011,6.5.9/6)

但不幸的是,这有点混乱。

尽管标准对==表达式的指针操作数的类型设置了约束,但它并不要求它们的值是有效的指针。为了避免对此有任何疑问,需要与6.3.2.3节的规定保持内部一致性,该节指定涉及空指针的相等比较的结果(不限于空指针常量)。

如果x == y的操作数中至少有一个是无效指针,而不是空指针,例如,我们可以假设,(T *)-1,那么6.5.9/6所给出的选项都不成立,所以表达式应该计算为0。编译器可能会使用它来对测试和分支进行优化。

然而,在实践中,在这方面,实现往往不一致。相反,他们从历史行为中得到启示,或许可以通过在6.5.9/6中对地址空间的短暂引用来证明自己的正当性,或者对物体是什么采取了自由的看法。对于提供地址空间平面视图的实现,无论这些地址与任何对象的关系如何,这都显示为正在计算的==,其指针值对应的地址是否相同。这样的实现不能优化==测试,因为它不能安全地假设它总是失败。

因此,底线是,尽管编译器不太可能优化测试,但您不能依靠标准来保证它不会这样做。如果您使用空指针作为哨兵,您将处于更安全的位置,因为尽管我在实践中呼吁的不一致性,但在所有实现中,相同类型的空指针在所有实现中都比较相等,根据6.3.2.3/4。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55615620

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档