【问题标题】:Pointer to integer conversion in Standard C标准 C 中指向整数转换的指针
【发布时间】:2016-03-30 06:08:59
【问题描述】:

我已经阅读了这个网络上关于这个主题的几个问题,但我仍然有疑问。

在标准 C 中我们可以阅读:

6.3.2.3.p.5:整数可以转换为任何指针类型。 [...]
6.3.2.3.p.6:任何指针类型都可以转换为整数类型。 [...]。

省略的文本(将放在括号 [...] 中)只是讨论了转换失败时可能发生的问题。

但是我的问题更笼统:

  1. 我观察到 整数数学数字 作为一组值是无限的。
  2. C 中的任何整数类型都只能表示整数数学值的有限子集。
  3. 在第 6.3.2.3.p5/p6 段中,标准 C11 似乎假定“每个指针值都可以映射到 整数数学值”。
  4. 此外,C11 标准的修订方式似乎表明只有当这个数学(或抽象)值不能以开发人员选择的预期整数类型表示时,无论出于何种原因,是操作失败的时候。

我的问题是:我在第 (3.) 点的解释是否正确?

另一方面,当我们定义一个大小为 NT 类型数组时,
因为我们可以对数组的位置进行整数运算,
结果很明显,至少在“本地”级别,在一个数组中,
我们有该数组的相应内存块的行为
算术上,与从 0N-1 的数字集相同的方式,比如说。

通过定义数组或分配的对象,我们可以确定,在 C 中,
内存地址可以“本地”被认为在算术上等同于整数子集。

但是,这种“本地”行为不足以得出结论
标准 C 假设一个内存模型,其内存地址可以被视为“只有一个且相同的整数集”的一部分。

然而,6.3.2.3.p5/p6 似乎“暗示”了这个更强的断言,尽管我并不完全确定。

另一个问题可能会带来更多启示:

  • 在标准C中打开了两个不同的有效指针pq(即:p != q)的可能性,当转换为整数类型时,得到的整数值变为相等强>。

【问题讨论】:

  • 你眼中的“整数非数学值”是什么?
  • @cad:我试图将抽象的值集(它是无限的并具有某些形式属性)与能够在 C 中以给定整数类型表示的值的特定子集区分开来。在给定类型中旨在成为“整数”的值的行为与数学“整数”不同。例如,无符号整数中的 UINT_MAX+1 == 0。行为不同,因此它们不是“数学”,而是紧密模拟。
  • 我认为你想太多了。我只记得指针可以转换为标准整数类型,其中只有uintptr_tintptr_t 保证能够保持其值。
  • 那么任何类型的“数学”如何?据我从您的解释中可以看出,纯粹的“数学”类型能够保存所有可能的整数值,这是不可能的,因为可用的内存量是有限的。
  • @cad:我不认为纯粹的“数学”值在 C 中具有类型。就像人们可以说“值(纯数学或抽象)1 亿不可表示一样在 64 位 unsigned long long 类型中”。这种语言模式只谈论“数学”或“抽象”数字,没有类型,但随后描述了给定的类型。在讨论指针到整数转换的问题时,我试图阐明这种讨论模式是否与第 6.3.2.5p.5/p.6 段中的相同。

标签: c pointers language-lawyer c99 c11


【解决方案1】:

奇怪的是,您将标准的某些部分视为“只是谈论转换失败时可能发生的问题”,而其中一些正是解决您问题的部分。

  1. 我观察到整数数学数字作为一组值是无限的。

正确,但无关紧要。标准说“任何指针类型都可以转换为 [any] 整数 type”(强调)。它专门指的是一系列 C 数据类型,包括但不限于 int。您从引用中省略的文本清楚地表明某些整数类型可能无法表示结果。事实上,它说“结果不必在 any 整数类型的值范围内”(强调)。

  1. C 中的任何整数类型都只能表示整数数学值的有限子集。

是的。

  1. 在第 6.3.2.3.p5/p6 段中,标准 C11 似乎假定“每个指针值都可以映射到整数数学值”。

不,事实上不是。您省略的部分文字是“ 结果是实现定义的”,用于双向转换,但有某些例外。这种转换的实现定义行为可能是程序退出。我接受标准建议可能存在一个或多个映射,例如你描述的,但不是假设有一个。

  1. 此外,C11 标准的修订方式似乎表明,只有当这个数学(或抽象)值不能以开发人员选择的 intedend 整数类型表示时,无论出于何种原因,操作失败。

操作永远不会失败。在许多情况下,行为是由实现定义的,在某些情况下是未定义的,但这完全不同。

但是请注意,该标准对选择提供可选intptr_t 和/或uintptr_t 类型的实现提出了更多要求,因为它们支持往返,将值保留void * 到整数类型到@ 987654325@ 转换。此类实现(很典型)必须从所有可能的指针值进行 1-1 映射,不仅映射到数学整数,而且映射到可表示的整数值。

【讨论】:

  • 谢谢你,约翰,我试图了解标准 C 是否认为内存在算术上类似于整数的唯一子集,或者它对更多额外的可能性持开放态度。另一方面,如果我对自己的问题不清楚,我很抱歉,但显然这是我在理解该主题时遇到的问题的一部分。
  • 回复。 (3), "结果是 i-d" --> "结果是 i-d 或 UB" 。 Re(4),将导致UB描述为“失败”是正常的
  • @pablo1977,底线是“否”,标准本身没有考虑(也没有直接谈论)内存寻址如何映射到整数。然而,实现通过直接映射到整数的平面地址空间对内存进行建模是很常见的,甚至可能与系统的整数类型之一具有相同的表示。
  • @JohnBollinger:谢谢你,约翰。
  • 如果“结果”是实现定义的,那么程序将不会退出。如果“行为”是实现定义的,那将是可能的。
【解决方案2】:

我的问题是:我在第 (3.) 点的解释是否正确?

该标准指定了uintptr_tintptr_t 类型,以便每个有效指针都可以转换为uintptr_t / intptr_t 并再次返回。比较 ISO 9899:2011 §7.20.1.4:

7.20.1.4 能够保存对象指针的整数类型

1 以下类型指定有符号整数类型,其属性是任何指向 void 的有效指针都可以转换为该类型,然后转换回指向 void 的指针,结果将与原始指针进行比较:

intptr_t

以下类型指定一个无符号整数类型,其属性是任何指向 void 的有效指针都可以转换为该类型,然后转换回指向 void 的指针,结果将与原始指针进行比较:

uintptr_t

这些类型是可选的。

但这些类型可能不存在,在这种情况下,不能保证每个指针都映射到一个整数。

在标准 C 中开放两个不同的有效指针 p、q(即:p != q)的可能性,当转换为整数类型时,得到的整数值变得相等。

是的,这可能发生。考虑一个像 8086 这样的平台,它有 20 位指针和 16 位 int。显然,在将指针转换为整数时,许多指针将被映射到同一个整数。如果您采用更大的类型,例如 32 位 long,那么您将获得每个指针的唯一值。

【讨论】:

  • 嗯。我可以在标准中读到您提到的这些整数类型是可选定义的。这就是我在问题中没有提到它们的原因。 (在标准 C11 中:7.20.1.4)。
  • 请注意,intptr_tuintptr_t可选的。一个 C 实现可以在不提供它们的情况下符合标准,实际上也没有任何类型可以像描述的那样。
  • @JohnBollinger 尝试刷新页面,我在几分钟前添加了关于此的评论。
  • “显然,当将指针转换为整数时,许多指针将被映射到同一个整数。” - 不一定,某些指针在尝试将它们转换为整数时可能会导致硬件异常或导致程序退出
  • @M.M 不适用于有效指针,绝对不适用于 8086(这就是我选择这个具体示例的原因)。
【解决方案3】:

您的p != q 问题的答案在第 6 段的 [...] 部分给出:

如果结果不能以整数类型表示,则行为 未定义。

因此,如果 pq 是两个不同的有效指针,并且您将这些指针转换为整数没有调用未定义的行为,那么生成的整数将不会比较相等。


您的主要问题的答案在注释 67(适用于第 5 段)中给出:

将指针转换为整数或整数的映射函数 指向指针的整数旨在与寻址一致 执行环境的结构。

在我知道的每个执行环境中,寻址结构都基于自然数的有限子集,其中 0 用作NULL 指针。因此,如果整数值的范围超出目标硬件中使用的地址范围,则可以保证从指针转换为整数类型。

【讨论】:

  • 因此,如果我们调用 未定义的行为,我们可以拥有例如 (unsigned long)p == (unsigned long) q,即使在 p!=q 的情况下(作为 p.q,指针) .
  • 可以,但unsigned long 可以与指针大小相同。
  • 是的,但是如果您调用未定义的行为,那么任何事情都可能发生,例如你的电脑着火了。调用未定义行为的程序没有用处。
  • 您的期望是合理的,但不是标准要求的。许多从指针到整数的转换都涉及实现定义的行为,而不是未定义的行为。实现定义的行为不一定是您所描述的。
  • "那么结果整数将不会比较相等。" - 这不是标准的要求 - 只有脚注文本(非规范性)表明它对实现尝试和实现这一点很有用
猜你喜欢
  • 1970-01-01
  • 2017-02-12
  • 2013-09-13
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多