简体   繁体   English

位字段结构的C ++严格别名规则

[英]C++ strict aliasing rule for bit field struct

Does the getValue() member function below violate the c++ strict aliasing rule? 下面的getValue()成员函数是否违反了c ++严格别名规则?

According to the standard, I believe setValue() violates the strict aliasing rule since double is neither an aggregate type nor the base class of IEEE754_64. 根据标准,我认为setValue()违反了严格的别名规则,因为double既不是聚合类型也不是IEEE754_64的基类。

What about getValue()? getValue()怎么样? Is it an undefined behavior when the data member is in bit field form as the example below? 当数据成员是位字段形式时,它是一个未定义的行为,如下例所示?

I am using similar code in a large project. 我在一个大型项目中使用类似的代码。 GCC -O2 and -O3 output the wrong value. GCC -O2和-O3输出错误的值。 And the issue is gone if I add -fno-strict-aliasing. 如果我添加-fno-strict-aliasing,问题就消失了。 Also, the issue is gone if I use memcpy instead of casting in getValue(). 此外,如果我使用memcpy而不是在getValue()中进行转换,问题就消失了。 Not sure if it is an GCC bug. 不确定它是否是GCC错误。

#include <iostream>
#include <cstring>

using namespace std;
struct IEEE754_64
{
  void setValue(double);
  unsigned long long getValue();
  // Data members
  unsigned long long d_mantissa : 52;
  long long d_exponent : 11;
  unsigned long long d_sign : 1;
};

void IEEE754_64::setValue(double d)
{
  (*this) = *reinterpret_cast<IEEE754_64*>(&d);
}

unsigned long long IEEE754_64::getValue()
{
  return * reinterpret_cast<unsigned long long *>(this);
}

int main()
{
  double b = 1.0;
  IEEE754_64 d;

  memcpy(&d, &b, sizeof(double));
  cout<<hex<<d.getValue()<<endl;

  d.setValue(1.0);
  cout<<hex<<d.getValue()<<endl;
  return 0;
}

The behaviour of reinterpret_cast<T *>(&a) depends on what object is actually at the memory location of a . 的行为reinterpret_cast<T *>(&a)依赖于实际上是在的存储位置什么物体a

Informally, if there is actually a T object there too (which can, of course, only happen if the T is a subobject of a or vice versa) then the result of the cast is a pointer to the T object. 非正式地,如果实际上有一个T对象有太多(其可以,当然,只有在发生T是的子对象a或者反之亦然),则铸造的结果是一个指向T对象。

Otherwise the result of the cast is a pointer to a with the wrong type and reading through it may violate the strict aliasing rule. 否则,中投的结果是指向a用了错误的类型,并通过它阅读可能违反严格别名规则。

Formally, the above is explained in the Standard under the sections [expr.static.cast]/13 , and [basic.compound]/4, see here for detail. 在形式上,上述内容在[expr.static.cast] / 13和[basic.compound] / 4部分的标准中进行了解释, 详见此处


With that in mind, setValue reads a double through an lvalue of type IEEE754_64 , there is no doubt that this is a strict aliasing violation. 考虑到这一点, setValue通过IEEE754_64类型的左值读取一个double ,毫无疑问这是一个严格的别名违规。

For the getValue case we have to understand the behaviour of reinterpret_cast<unsigned long long *>(this) which is less straight forward. 对于getValue情况,我们必须理解reinterpret_cast<unsigned long long *>(this)的行为,这不太直接。

According to [basic.compound]/4, an object and its first non-static data member are always pointer-interconvertible. 根据[basic.compound] / 4,对象及其第一个非静态数据成员始终是指针可互换的。 It does not list any exception for bitfields. 它没有列出任何位域异常。

But the relevant text from [expr.static.cast]/13 is: 但[expr.static.cast] / 13的相关文字是:

Otherwise, if the original pointer value points to an object a , and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a , the result is a pointer to b . 否则,如果原始指针值指向对象a ,并且存在类型为T (忽略cv-qualification)的对象b ,该对象b是指针可互换a ,则结果是指向b的指针。

If we accept that the bit-field is "an object of type unsigned long long ", it follows that the result of the cast is a pointer to a bit-field. 如果我们接受位字段是“ unsigned long long类型的对象”,那么强制转换的结果就是指向位字段的指针。 However the Standard does not define the behaviour of pointers to bit-fields. 但是,标准没有定义指向位字段的指针的行为。

So, IMHO, the best way to interpret the above text is to say that the bit-field is not an object of type unsigned long long . 所以,恕我直言,解释上述文本的最佳方式是说位字段不是unsigned long long类型的对象。 I believe this is consistent with the rest of the standard; 我相信这与标准的其他部分一致; even though there are no prvalues of bit-field type , it definitely does talk about glvalues of bit-field type. 即使没有位字段类型的prvalues,它肯定会讨论位字段类型的glvalues。

Summing up; 加起来; I believe the result of reinterpret_cast<unsigned long long *>(this) is NOT a pointer to this->d_mantissa , therefore the getValue() function accesses an object of type IEEE754_64 using a glvalue of type unsigned long long , violating the strict aliasing rule. 我相信reinterpret_cast<unsigned long long *>(this)的结果不是指向this->d_mantissa的指针,因此getValue()函数使用unsigned long long类型的IEEE754_64访问类型为IEEE754_64的对象,违反了严格别名规则。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM