首页 > 解决方案 > 将指针转换为数据类型时的实现特定行为

问题描述

据我了解,根据 C 标准,不建议将 int 指针转换为 int 用于可移植代码。一个简单的例子是在 64 位架构上进行这种转换,其中指针是 64 位但整数类型是 32 位,在这种情况下,转换会截断信息,即事情如何出错的实际物理示例

将整数转换为 int 指针也是如此。但是,我找不到一个例子来说明为什么这被认为是特定于 UB/实现的。我知道 C 标准不建议这样做,但究竟会出现什么问题呢?我发现的唯一模糊的例子是有人提到可能的对齐问题,这些问题究竟是如何出现的?

标签: cundefined-behavior

解决方案


C 标准在列出可能的问题方面相当详细,C17 6.3.2.3/5:

整数可以转换为任何指针类型。除非前面指定,结果是实现定义的,可能没有正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。

所以各种潜在的问题是:

  • 不同的尺寸。这是最明显的问题。指针地址可能不适合整数,反之亦然。

  • 结盟。包含一些随机数的整数在转换为指针时可能会导致地址未对齐。

  • 不正确的地址,例如未对齐的地址或指向可执行代码而不是数据的地址,可能会导致实现定义的“陷阱”/硬件异常。

    理论上,整数也不太可能包含陷阱表示,但这可能只与奇异/虚构的补码和带符号的幅度系统相关。C 标准允许这样的系统,但在计算机的历史上实际上很少有这样的系统存在。

  • 类型错误。如果我们在转换到/从指针时对编译器撒谎并告诉它在某个位置存储了另一种类型而不是实际存储在那里,我们可能会遇到各种问题。我们可能会搞砸编译器对存储在哪里的类型的内部跟踪,即所谓的“严格指针别名违规”。这反过来可能会导致与优化相关的错误。

    什么是严格的别名规则?

    我们也可能再次导致未对齐和陷阱问题,或者只是由于程序无法理解特定位置存储的内容。

  • 未知地址上的指针算术。转换到编译器不知道存储什么(未知类型)然后从那里执行指针算术的物理地址可能存在问题。因为指针运算仅在指向已知类型的数组时才被明确定义。严格来说,这样做是未定义的行为,因此可能会导致一些糟糕的编译器实现出错并产生随机行为代码。众所周知,托管系统编译器会这样做——这是一个实施质量问题。特别是在使用 gcc 编译器进行嵌入式系统编程时,要非常害怕这样的错误。

  • 异国情调的指针格式。一些系统使用超出默认地址总线宽度的扩展寻址模式。这在具有 8-/16 位地址的低端嵌入式系统中很常见,但早在 MS DOS 时代,PC 世界中也存在这种情况。通常,此类扩展地址使用非标准指针类型(常见的非标准扩展是far关键字)。与这些指针类型和整数之间的转换将是非常系统特定的。

用于与指针类型相互转换的最正确类型是uintptr_t. 这被定义为“足够大”并且适合保存指针的表示。

在一些奇异的系统上,我们也可以使用intptr_t它是有符号的等价物。只有当操作系统有一些奇怪的内部虚拟寻址时,那个才有意义,比如将内核空间放在负地址。


推荐阅读