似乎指针的低位为0或多或少是相当可移植的(可移植性显然并不意味着“标准”,但人们已经舍弃了它,并在某些情况下可以利用它,从而有一定的优势,希望禁用它-能够使用编译开关)。
想要变得愚蠢的项目使用了它,倒数第二低的运气是:
How portable is using the low bit of a pointer as a flag?
但是,可以说,我们不想只是戳一点数据或不戳入已知类型的指针。您希望做的是使用该低位为0,以允许指针类型作为终止符执行“双重责任”。
因此,您的商品如下所示:
struct Item {
uintptr_t flags; // low bit zero means "not an item"
type1 field1;
type2 field2;
...
};
然后,您会遇到一些项目容器如下所示的情况:
[(flags field1 field2...) (flags field1 field2...) some-pointer stuff stuff...]
这样,您就可以摆脱“沉没成本”(假设数据结构中有一些内部管理指针用于其他目的)来为您终止工作。
更新:为了更清楚地了解这种情况:这是控制代码库和结构的地方。因此,可以将这样使用的结构中的任何指针声明为联合类型,例如:
union Maybe_Terminator_Pointer {
uintptr_t flags;
type1* pointer1;
type2* pointer2;
...
};
...然后使用它(如果有帮助)。排除
char*
很好,因为它们当然不算在内。因此,这里存在一个额外的类型校正问题:用于进行终止测试的指针是
Item*
,而进行检查的例程不知道哪种指针some-pointer
是特定的。我想知道,最好的赌博是能够移植和编译这种技巧。这包括将指针转换为并集,#ifdef定义机器的字节序,并使用位从字节中获取char *,等等。如果有人有经验或猜测,则更可能行得通。
想象一下,为您的案例节省大量数据是值得的。而且您有一个备用方案,即如果人们发现该技巧在某处不起作用,那么#ifdef可能会使用全尺寸的项目作为终止符并浪费额外的空间。因此,想知道是否有任何技巧可以使这个明显违反标准的技巧在更多系统上工作的机会更大。
最佳答案
(自我回答以提供更多信息,并允许人们发现我的替代品中的任何潜在问题。)
因此,想知道是否有任何技巧可以使这个明显违反标准的技巧在更多系统上工作的机会更大。
提示(根据评论)如果您可能会找到其他方法,请不要这样做。
例如,“您”提到此布局:
[(flags field1 field2...) (flags field1 field2...) some-pointer stuff stuff...]
但是“
stuff stuff
”中是否有不是指针的东西—可能是无聊的已知整数甚至是偶数—在这里您可以做同样的事情?如果是这样,为什么不像这样重新排序:[(flags field1 field2...) (flags field1 field2...) even-uintptr_t stuff...]
这样,无论您是否从
flags
中读取Item
,它都是相同的类型。如果您四处查看指针等并非不透明的事物,那么您可能会在当前代码中发现明显的不透明数字,这些数字始终是偶数...例如,很多保证聚合的字节数都是可以保证的作为% 2 = 0
。技巧2适用于上述替代方法-也许也有助于解决标准破坏指针版本的问题。确保在写入值时使用“别名”指针,并且不要直接通过
.
或->
写入字段。不需要编译器确保字段上两个不同结构之间的内存一致性,只是因为它们是同一类型。说
struct A
以uintptr_t field_a
开头,而struct B
以uintptr_t field_b
开头,然后将两个指针都放在同一地址。如果执行some_a->field_a = value;
,则从该地址的some_b->field_b
指针回读可能不会看到该更新,因为编译器不希望您通过A指针写入B字段。因此,通过指针进行写操作。像
uintptr_t *alias = &some_a->field_a;
然后是*alias = value
之类的东西将与连续读取任何整数(!)的内容保持一致。 (对指针is why restrict
exists的此属性的性能后果不满意。如果此技巧奏效,则只能通过利用指针的非限制行为来做到这一点。)(!)-我认为您只需要通过指针执行写入操作,而不必进行读取操作,但是也许有人可以提供见解。
关于c - 读取低指针位的方式可能(可能)在尽可能多的系统上工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34704083/