12

一些动态类型语言使用指针标记作为一种快速方法来识别或缩小所表示值的运行时类型。执行此操作的经典方法是将指针转换为适当大小的整数,并在最低有效位上添加标记值,对于对齐的对象,最低有效位假定为零。当需要访问对象时,标记位被屏蔽掉,整数被转换为指针,指针被正常解除引用。

这本身就是有序的,除了它都取决于一个巨大的假设:对齐的指针将转换为保证在正确位置具有零位的整数。

是否可以根据标准的字母来保证这一点?


尽管标准第 6.3.2.3 节(参考 C11 草案)说从指针到整数的转换结果是实现定义的,但我想知道的是 6.5.2.1 和 6.5.6 中的指针算术规则是否有效约束指针->整数转换的结果遵循许多程序已经假定的相同的可预测算术规则。(6.3.2.3 note 67 似乎暗示这是该标准的预期精神,但这并不意味着什么。)

我特别考虑了一个可能分配一个大数组作为动态语言的堆的情况,因此我们正在谈论的指针指向这个数组的元素。我假设 C 分配的数组本身的开头可以通过某种辅助方式放置在对齐的位置(尽管也可以讨论这个)。假设我们有一个 8 字节的“cons 单元”数组;我们能否保证指向任何给定单元格的指针将转换为一个整数,其中最低三位可用于标记?

例如:

typedef Cell ...; // such that sizeof(Cell) == 8
Cell heap[1024];  // such that ((uintptr_t)&heap[0]) & 7 == 0

((char *)&heap[11]) - ((char *)&heap[10]); // == 8
(Cell *)(((char *)&heap[10]) + 8);         // == &heap[11]
&(&heap[10])[0];                           // == &heap[10]
0[heap];                                   // == heap[0]

// So...
&((char *)0)[(uintptr_t)&heap[10]];        // == &heap[10] ?
&((char *)0)[(uintptr_t)&heap[10] + 8];    // == &heap[11] ?

// ...implies?
(Cell *)((uintptr_t)&heap[10] + 8);        // == &heap[11] ?

(如果我理解正确,如果实现提供,uintptr_t那么 6.3.2.3 第 6 段中暗示的未定义行为是无关紧要的,对吧?)

如果所有这些都成立,那么我认为这意味着您实际上可以依赖任何转换后的指向对齐数组元素的指针的低位Cell来自由进行标记。他们 && 会吗?

(据我所知,这个问题是假设性的,因为正常的假设无论如何都适用于通用平台,如果你找到了一个没有的地方,你可能不想参考 C 标准而不是平台文档;但这不是重点。)

4

2 回答 2

19

这本身就是有序的,除了它都取决于一个巨大的假设:对齐的指针将转换为保证在正确位置具有零位的整数。

是否可以根据标准的字母来保证这一点?

实现可以保证这一点。将指针转换为整数的结果是实现定义的,实现可以任意定义它,只要它符合标准的要求。

该标准绝对不能保证这一点。

一个具体的例子:我在 Cray T90 系统上工作过,它有一个 C 编译器,在类 UNIX 操作系统下运行。在硬件中,地址是一个包含 64 位字地址的 64 位字;没有硬件字节地址。字节指针 ( void*, char*) 是通过在 64 位字指针的其他未使用的高 3 位中存储 3 位偏移量而在软件中实现的。

所有指针到指针、指针到整数和整数到指针的转换都只是简单地复制了表示。

这意味着指向 8 字节对齐对象的指针在转换为整数时,可以在其低 3 位中具有任何位模式。

标准中没有任何内容禁止这样做。

底线:如果您对当前系统如何表示指针做出某些假设,那么像您描述的那样玩指针表示游戏的方案就可以工作- 只要这些假设恰好对当前系统有效。

但是没有这样的假设是 100% 可靠的,因为标准没有说明指针是如何表示的(除了它们对于每个指针类型都是固定大小的,并且表示可以被视为 的数组unsigned char)。

(该标准甚至不保证所有指针的大小都相同。)

于 2013-09-02T17:47:09.793 回答
5

您对标准的相关部分是正确的。以供参考:

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

任何指针类型都可以转换为整数类型。除非前面指定,结果是实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。

由于转换是实现定义的(除非整数类型太小,在这种情况下它是未定义的),因此标准不会告诉您有关此行为的任何内容。如果您的实施做出了您想要的保证,那么您已经准备就绪。否则,太糟糕了。

我猜你的明确问题的答案:

是否可以根据标准的字母来保证这一点?

是“是”,因为标准对此行为进行了抨击,并表示实现必须对其进行定义。可以说,出于同样的原因,“不”也是一个很好的答案。

于 2013-09-02T17:37:15.453 回答