根据标准,C中的指针标记是否未定义?

Leu*_*nko 11 c standards-compliance language-lawyer

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

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

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


虽然标准的6.3.2.3节(参考C11草案)说从指针到整数的转换结果是实现定义的,但我想知道的是6.5.2.1和6.5.6中的指针算术规则是否有效约束指针 - >整数转换的结果遵循许多程序已经假设的相同的可预测算术规则.(6.3.2.3注释67似乎表明这是标准的预期精神,而不是那意味着很多.)

我特别想到的情况是,人们可能会分配一个大型数组作为动态语言的堆,因此我们所讨论的指针是这个数组的元素.我假设C分配数组本身的开始可以通过一些辅助手段放置在对齐位置(尽管如此也是这样讨论).假设我们有一个八字节"cons cell"数组; 我们可以保证指向任何给定单元格的指针将转换为标记最低三位的整数吗?

例如:

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] ?
Run Code Online (Sandbox Code Playgroud)

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

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

(据我所知,这个问题是假设的,因为无论如何普通假设都适用于普通平台,如果你找到了一个没有的假设,你可能不希望看C标准而不是平台文档;但这不是重点.)

Kei*_*son 18

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

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

这是可能的实现来保证这一点.将指针转换为整数的结果是实现定义的,并且实现可以按照自己喜欢的方式定义它,只要它符合标准的要求即可.

标准绝对不能保证这一般.

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

所有指向指针的指针,指向整数的指针和整数到指针的转换只是复制了表示.

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

标准中没有任何内容禁止这一点.

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

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

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

  • 一切都很糟糕. (2认同)

Car*_*rum 5

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

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

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

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

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

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

是“是”,因为标准对这种行为嗤之以鼻,并说实现必须定义它。可以说,出于同样的原因,“不”也是一个很好的答案。