严格的指针混叠:针对特定问题的任何解决方案?
-
24-10-2019 - |
题
我遇到了严格的指针混叠规则引起的问题。我有类型 T
来自模板和一些积分类型 Int
大小相同(与 sizeof
)。我的代码本质上可以执行以下操作:
T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
...
因为 T
是可能具有构造函数的某些arbitarity(尺寸限制)类型,我不能结合 T
和 Int
. 。 (仅在C ++ 0x中允许,甚至不受GCC的支持)。
有什么办法可以重写上述伪代码来保留功能并避免破坏严格的别名规则?请注意,这是一个模板,我无法控制 T
或价值 some_other_t
;分配和后续比较确实发生在模板的代码中。
(根据记录,上述代码开始在GCC 4.5上破裂 T
包含任何位字段。)
解决方案
static inline int is_T_0(const T *ob)
{
int p;
memcpy(&p, ob, sizeof(int));
return p == 0;
}
void myfunc(void)
{
T x = some_other_t;
if (is_T_0(&x))
...
在我的系统上,GCC优化两者 is_T_0()
和 memcpy()
, ,在 myfunc()
.
其他提示
你听说过吗 boost::optional
?
我必须承认,我在这里不清楚的是真正的问题...但是boost ::可选允许按价值存储,但知道是否已经初始化了实际内存。我还允许施工和破坏,我想这可能是一个很好的合适。
编辑:
我想我终于解决了这个问题:您希望能够在内存中的各个点分配许多对象,并且您想知道此时的内存是否真的持有对象。
不幸的是,您的解决方案有一个巨大的问题:这是不正确的。如果曾经 T
可以以某种方式由 null
位模式,然后您会认为它是一个统一的内存。
您将不得不求助于至少添加一点点信息。毕竟,这并不多,仅占增长的3%(4个字节33位)。
例如,您可以使用一些模仿 boost::optional
但是以阵列方式(避免填充损失)。
template <class T, size_t N>
class OptionalArray
{
public:
private:
typedef unsigned char byte;
byte mIndex[N/8+1];
byte mData[sizeof(T)*N]; // note: alignment not considered
};
然后,这很简单:
template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
return mIndex[i/8] & (1 << (i%8));
}
template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
assert(!this->null(i));
return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}
笔记: :为简单起见,我没有考虑过对齐问题。如果您不知道这个主题,请在摆弄内存之前阅读:)
这个怎么样:
Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)
它可能不那么高效,但应该摆脱警告。
附录1:
自从 T
被限制为与 Int
, ,使自己成为类型的虚拟位零值 T
并直接与之进行比较(而不是铸造和比较agaist Int(0)
).
如果您的程序是单线程,则可以拥有这样的东西:
template <typename T>
class Container
{
public:
void foo(T val)
{
if (zero_ == val)
{
// Do something
}
}
private:
struct Zero
{
Zero() {memset(&val, 0, sizeof(val));}
bool operator==(const T& rhs) const {return val == rhs;}
T val;
};
static Zero zero_;
};
如果是多线程,您需要避免使用静态成员 zero_
, ,并让每个容器实例保持自己的 zero_
成员:
template <typename T>
class MTContainer
{
public:
MTContainer() {memset(zero_, 0, sizeof(zero_));}
void foo(T val)
{
if (val == zero_)
{
// Do something
}
}
private:
T zero_;
};
附录2:
让我以另一种更简单的方式将上述附录放置:
// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));
T x = some_other_t;
if (x == zero)
为什么不简单:
const Int zero = 0;
if (memcmp(&some_other_t, &zero, sizeof(zero)) == 0)
/* some_other_t is 0 */
(您可能还想尝试添加 static
预选赛 zero
看看它是否在性能方面有所不同)
使用33位计算机。 ; p
感觉就像是一个黑客,但显然我找到了一个解决方案:使用 volatile
为了 Int
铸件。本质上,我现在正在做的是:
T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
...
比特菲尔德的问题 T
现在消失了。不过,我对此并不感到高兴 volatile
在C ++ AFAIK中没有明确定义...