将指针转换为数据类型时的特定实现行为
Implementation specific behavior when casting a pointer to a data type
据我了解,根据 C 标准,不建议将 int 指针转换为 int 用于可移植代码。一个简单的例子是在 64 位架构上进行这样的转换,其中指针是 64 位但整数类型是 32 位,在这种情况下转换会截断信息,即事情如何出错的实际物理示例
将整数转换为int指针也是如此。但是,我找不到一个例子来说明为什么这被认为是 UB/implementation 特定的。我得到了 C 标准的反对意见,但究竟会出什么问题呢?我发现的唯一模糊的例子是有人提到了可能的对齐问题,这些问题到底是怎么出现的?
并非所有平台都具有相同的大小 int
和 int*
。这可能会导致截断和对齐问题等。它似乎也可以毫无问题地工作。
对于可移植行为,建议使用 <stdint.h>
中 C99 标准定义的 fixed-width 整数。
您可以使用 uintptr_t
作为变量来保存 pointer-address。
也看到这个答案:When is casting between pointer types not undefined behavior in C?
C 标准在列出可能的问题方面相当详细,C17 6.3.2.3/5:
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
所以各种潜在的问题是:
尺寸不同。这是最明显的问题。指针地址可能不适合整数,反之亦然。
对齐。包含一些随机数的整数在转换为指针时可能会导致地址未对齐。
不正确的地址,例如未对齐的地址或指向可执行代码而不是数据的地址,可能会导致实现定义的“陷阱”/硬件异常。
整数在理论上也可以包含陷阱表示的可能性很小,但这可能只与 exotic/fictional 一个补码和带符号的幅度系统相关。 C标准允许这样的系统,但是在计算机的历史上真正存在的这样的系统非常少
类型错误。如果我们在转换 to/from 指针时欺骗编译器并告诉它在某个位置存储了另一种类型而不是实际存储在那里的类型,我们可能会遇到各种各样的问题。我们可能会搞砸编译器对哪些类型存储在哪里的内部跟踪,即所谓的“严格指针别名违规”。这反过来可能会导致 optimization-related 个错误。
What is the strict aliasing rule?
我们也可能再次导致错位和陷阱问题,或者只是程序无法理解特定位置存储的内容。
未知地址的指针运算。转换到编译器不知道存储的内容(未知类型)然后从那里执行指针运算的物理地址可能会出现问题。因为当指针指向一个已知类型的数组时,指针运算只是 well-defined 。严格来说,这样做是未定义的行为,因此它可能会导致一些糟糕的编译器实现出现错误并产生随机行为代码。众所周知,托管系统编译器会执行此操作——这是实现质量问题。尤其是在使用gcc编译器进行嵌入式系统编程的时候,非常害怕这样的bug。
奇异的指针格式。一些系统使用扩展寻址模式,超出默认地址总线宽度。这在具有 8-/16 位地址的 low-end 嵌入式系统中非常常见,但在 MS DOS 时代的 PC 世界中也存在。通常,此类扩展地址使用 non-standard 指针类型(常见的 non-standard 扩展是 far
关键字)。转换to/from这些指针类型和整数会很system-specific.
用于转换 to/from 指针类型的最正确类型是 uintptr_t
。这被定义为“足够大”并且适合容纳指针的表示。
在一些奇异的系统上,我们也可以使用 intptr_t
,这是有符号的等价物。只有 OS 有一些奇怪的内部虚拟寻址,比如将内核 space 放在负地址时,这个才有意义。
据我了解,根据 C 标准,不建议将 int 指针转换为 int 用于可移植代码。一个简单的例子是在 64 位架构上进行这样的转换,其中指针是 64 位但整数类型是 32 位,在这种情况下转换会截断信息,即事情如何出错的实际物理示例
将整数转换为int指针也是如此。但是,我找不到一个例子来说明为什么这被认为是 UB/implementation 特定的。我得到了 C 标准的反对意见,但究竟会出什么问题呢?我发现的唯一模糊的例子是有人提到了可能的对齐问题,这些问题到底是怎么出现的?
并非所有平台都具有相同的大小 int
和 int*
。这可能会导致截断和对齐问题等。它似乎也可以毫无问题地工作。
对于可移植行为,建议使用 <stdint.h>
中 C99 标准定义的 fixed-width 整数。
您可以使用 uintptr_t
作为变量来保存 pointer-address。
也看到这个答案:When is casting between pointer types not undefined behavior in C?
C 标准在列出可能的问题方面相当详细,C17 6.3.2.3/5:
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
所以各种潜在的问题是:
尺寸不同。这是最明显的问题。指针地址可能不适合整数,反之亦然。
对齐。包含一些随机数的整数在转换为指针时可能会导致地址未对齐。
不正确的地址,例如未对齐的地址或指向可执行代码而不是数据的地址,可能会导致实现定义的“陷阱”/硬件异常。
整数在理论上也可以包含陷阱表示的可能性很小,但这可能只与 exotic/fictional 一个补码和带符号的幅度系统相关。 C标准允许这样的系统,但是在计算机的历史上真正存在的这样的系统非常少
类型错误。如果我们在转换 to/from 指针时欺骗编译器并告诉它在某个位置存储了另一种类型而不是实际存储在那里的类型,我们可能会遇到各种各样的问题。我们可能会搞砸编译器对哪些类型存储在哪里的内部跟踪,即所谓的“严格指针别名违规”。这反过来可能会导致 optimization-related 个错误。
What is the strict aliasing rule?
我们也可能再次导致错位和陷阱问题,或者只是程序无法理解特定位置存储的内容。
未知地址的指针运算。转换到编译器不知道存储的内容(未知类型)然后从那里执行指针运算的物理地址可能会出现问题。因为当指针指向一个已知类型的数组时,指针运算只是 well-defined 。严格来说,这样做是未定义的行为,因此它可能会导致一些糟糕的编译器实现出现错误并产生随机行为代码。众所周知,托管系统编译器会执行此操作——这是实现质量问题。尤其是在使用gcc编译器进行嵌入式系统编程的时候,非常害怕这样的bug。
奇异的指针格式。一些系统使用扩展寻址模式,超出默认地址总线宽度。这在具有 8-/16 位地址的 low-end 嵌入式系统中非常常见,但在 MS DOS 时代的 PC 世界中也存在。通常,此类扩展地址使用 non-standard 指针类型(常见的 non-standard 扩展是
far
关键字)。转换to/from这些指针类型和整数会很system-specific.
用于转换 to/from 指针类型的最正确类型是 uintptr_t
。这被定义为“足够大”并且适合容纳指针的表示。
在一些奇异的系统上,我们也可以使用 intptr_t
,这是有符号的等价物。只有 OS 有一些奇怪的内部虚拟寻址,比如将内核 space 放在负地址时,这个才有意义。