21

例如,如果有一个类需要一个指针和一个bool. 为简单起见int,示例中将使用指针,但指针类型无关紧要,只要它指向size()大于 1 的东西。

使用数据成员定义类{ bool , int *}将导致类的大小是指针大小的两倍,并浪费大量空间

如果指针不指向 a char(或 的其他数据size(1)),那么大概低位将始终为零。该类可以定义为{int *}或为方便起见:union { int *, uintptr_t }

bool是通过根据逻辑bool值设置/清除指针的低位并在需要使用指针时清除该位来实现的。

定义的方式:

struct myData
{
 int * ptr;
 bool flag;
};
myData x;

// initialize
x.ptr = new int;
x.flag = false;

// set flag true
x.flag = true;

// set flag false
x.flag = false;

// use ptr
*(x.ptr)=7;

// change ptr
x = y;                // y is another int *

以及建议的方式:

union tiny
{
 int * ptr;
 uintptr_t flag;
};
tiny x;

// initialize
x.ptr = new int;

// set flag true
x.flag |= 1;

// set flag false
x.flag &= ~1;

// use ptr
tiny clean=x;      // note that clean will likely be optimized out
clean.flag &= ~1;  // back to original value as assigned to ptr
*(clean.ptr)=7;

// change ptr
bool flag=x.flag;
x.ptr = y;             // y is another int *
x.flag |= flag;

这似乎是未定义的行为,但它的可移植性如何?

4

4 回答 4

13

只要您在尝试将指针用作指针之前恢复指针的低位,只要您的系统、您的 C++ 实现和您的代码满足某些假设,它就可能是“合理的”可移植的。

我不一定能给你一个完整的假设清单,但我不以为然:

  • 它假定您没有指向大小为 1 字节的任何内容。这不包括char, unsigned char, signed char,int8_tuint8_t。(假设CHAR_BIT == 8在具有 16 位或 32 位字节的奇异系统上,可能会排除其他类型。)
  • 它假定大小至少为 2 个字节的对象始终在偶数地址对齐。请注意,x86不需要这个;您可以在奇数地址访问 4 字节int,但速度会稍慢。但是编译器通常会安排对象存储在偶数地址。其他架构可能有不同的要求。
  • 它假定指向偶数地址的指针的低位设置为 0。

对于最后一个假设,我实际上有一个具体的反例。在 Cray 向量系统(J90、T90 和 SV1 是我自己使用的)上,机器地址指向一个 64 位字,但 Unicos 下的 C 编译器设置了CHAR_BIT == 8. 字节指针在软件中实现,字中的 3 位字节偏移存储在64 位指针的其他未使用的高3 位中。因此,指向 8 字节对齐对象的指针可以轻松地将其低位设置为 1。

已经有 Lisp 实现(示例)使用指针的低 2 位来存储类型标记。我隐约记得这会在移植过程中造成严重问题。

底线:对于大多数系统,您可能可以摆脱它。未来的架构在很大程度上是不可预测的,我可以很容易地想象你的计划会在下一个大新事物上打破。

需要考虑的一些事项:

您可以将布尔值存储在类外的位向量中吗?(保持指针与位向量中相应位之间的关联留作练习)。

如果看到低位设置为 1 的指针,请考虑将代码添加到所有失败并显示错误消息的指针操作。#ifdef用于删除生产版本中的检查代码。如果您开始在某个平台上遇到问题,请构建启用检查的代码版本,然后看看会发生什么。

我怀疑,随着您的应用程序的增长(它们很少缩小),您需要存储的不仅仅是 abool和指针。如果发生这种情况,空间问题就会消失,因为无论如何您已经在使用额外的空间了。

于 2013-11-15T02:05:43.833 回答
4

在“理论”中:据我所知,这是未定义的行为。

在“现实”中:它可以在日常 x86/x64 机器上运行,也可能在 ARM 上运行?
除此之外,我真的无法发表任何声明。

于 2013-11-15T00:53:57.200 回答
3

它非常便携,此外,您可以assert在接受原始指针时确保它满足对齐要求。这将确保以某种方式搞砸你的深不可测的未来编译器。

不这样做的唯一原因是与“hacky”类似的东西相关的可读性成本和一般维护。除非有明显的收获,否则我会回避它。但这有时是完全值得的。

于 2015-09-15T09:30:36.783 回答
0

符合这些规则,它应该非常便携。

于 2013-11-15T00:54:52.080 回答