【问题标题】:Casting double array to a struct of doubles将双精度数组转换为双精度结构
【发布时间】:2015-09-13 22:49:52
【问题描述】:

可以将双精度数组强制转换为双精度结构吗?

struct A
{
   double x;
   double y;
   double z;
};

int main (int argc , char ** argv)
{
   double arr[3] = {1.0,2.0,3.0};
   A* a = static_cast<A*>(static_cast<void*>(arr));
   std::cout << a->x << " " << a->y << " " << a->z << "\n";
}

这打印出1 2 3。但它是否保证每次都能与任何编译器一起工作?

编辑:根据

9.2.21:指向标准布局结构对象的指针,经过适当转换?使用 reinterpret_cast,指向其初始成员 (...),反之亦然。

如果我用

替换我的代码
struct A
{
  double & x() { return data[0]; }
  double & y() { return data[1]; }
  double & z() { return data[2]; }
private:
   double data[3];
};

int main (int, char **)
{
   double arr[3] = {1.0,2.0,3.0};
   A* a = reinterpret_cast<A*>(arr);
   std::cout << a->x() << " " << a->y() << " " << a->z() << "\n";
}

那么它保证工作。正确的?我知道很多人不会觉得这种美学上令人愉悦,但是使用结构并且不必复制输入数组数据是有优势的。我可以在该结构中定义成员函数来计算标量和向量积、距离等,这将使我的代码比使用数组更容易理解。

怎么样

int main (int, char **)
{
   double arr[6] = {1.0,2.0,3.0,4.0,5.0,6.0};
   A* a = reinterpret_cast<A*>(arr);
   std::cout << a[0].x() << " " << a[0].y() << " " << a[0].z() << "\n";
   std::cout << a[1].x() << " " << a[1].y() << " " << a[1].z() << "\n";
}

这是否也可以保证工作或者编译器可以在数据成员之后放置一些东西以便sizeof(A) &gt; 3*sizeof(double)?是否有任何可移植的方式来防止编译器这样做?

【问题讨论】:

  • 任何人都可以编写编译器,所以这是一个棘手的问题。为什么不指定您要使用的编译器?我的猜测是所有主要的编译器都处理这个问题并且它确实有效。
  • 由于data structure alignment,很可能不会(如果您不仅想在那里使用双倍的话)
  • 你违反了严格的别名规则。

标签: c++ arrays struct language-lawyer static-cast


【解决方案1】:

不,不能保证。

唯一禁止任何编译器在xy 之间或yz 之间插入填充是常识。任何语言标准中都没有禁止它的规则。

即使没有填充,即使A 的表示与double[3] 的表示完全相同,那么它仍然无效。该语言不允许您假装一种类型真的是另一种类型。您甚至不允许将 struct A { int i; }; 的实例视为 struct B { int i; };

【讨论】:

  • 在这种情况下,Astandard layout。理论上可以有填充(double 不太可能,尽管long double 可能)但在任何情况下结构和数组都将具有相同的填充。这一切都假设没有#pragma 来覆盖对齐规则。 AB are.layout-compatible 也相互兼容(虽然不与数组。)
【解决方案2】:

该标准对对象的内存布局几乎没有保证。

对于类/结构:

9.2./15: 分配具有相同访问控制的类的非静态数据成员,以便后面的成员具有更高的地址 在一个类对象中。非静态数据的分配顺序 未指定具有不同访问控制的成员。执行 对齐要求可能会导致两个相邻的成员不能紧挨着分配;所以可能要求 用于管理虚函数和虚基类的空间。

对于数组,元素是连续的。没有任何关于对齐的说法,所以它可能会或可能不会使用与 struct 中相同的对齐规则:

8.3.4:数组类型的对象包含一个连续分配的非空集合,由 N 个类型为 T 的子对象组成。

在您的具体示例中,您唯一可以确定的是 a.x 对应于 arr[0],如果使用 reinterpret_cast:

9.2.21: 指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员 (...) 反之亦然。 [
>

【讨论】:

    【解决方案3】:

    不,它不能保证,即使它应该适用于我所知道的所有常见架构上的编译器,因为 C 语言规范说:

    6.2.6 类型的表示 6.2.6.1 概述1 所有类型的表示都是未指定的,除非本子条款中另有说明。它没有说明结构中的默认填充。

    当然,常见的架构最多使用 64 位,这是那些架构上双精度的大小,所以应该没有填充,并且您的转换应该可以工作。

    但是当心:你明确地调用了未定义的行为,下一代编译器在编译这样的转换时可以做任何事情。

    【讨论】:

      【解决方案4】:

      据我所知,答案是:是的。

      唯一会让你失望的是#pragma 指令,它为结构设置了一些非常不寻常的对齐设置。例如,如果一个 double 在您的机器上占用 8 个字节,并且 #pragma 指令告诉将每个成员对齐在 16 字节边界上,这可能会导致问题。除此之外你很好。

      【讨论】:

        【解决方案5】:

        msvc的std::complex实现使用数组解,llvm libc++使用前一种形式。

        我认为,只需检查 libc++ 的 std::complex 的实现,并使用相同的解决方案。

        【讨论】:

          【解决方案6】:

          我不同意这里的共识。一个包含三个双精度的结构与一个包含三个双精度的数组完全相同。除非您专门以不同的方式打包结构,并且在一个奇怪的处理器上,该处理器具有奇数字节的双精度。

          它没有内置在语言中,但我会觉得这样做很安全。风格明智我不会这样做,因为它只是令人困惑。

          【讨论】:

            猜你喜欢
            • 2011-07-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多