将双精度数组转换为双精度结构

问题描述 投票:0回答:6

可以将双精度数组转换为由双精度数组成的结构吗?

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) > 3*sizeof(double)
?有没有任何可移植的方法来阻止编译器这样做?

c++ arrays language-lawyer static-cast type-punning
6个回答
9
投票

不,不能保证。

唯一禁止任何编译器在

x
y
之间或
y
z
之间插入填充的事情是常识。任何语言标准中都没有规则不允许这样做。

即使没有填充,即使

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


4
投票

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

对于类/结构:

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

对于数组,元素是连续的。没有提到对齐,因此它可能会或可能不会使用与 struct 中相同的对齐规则:

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

在具体示例中,您唯一可以确定的是,如果使用reinterpret_cast,

a.x
对应于
arr[0]

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


1
投票

不,不能保证,即使它应该与我所知道的通用架构上的所有编译器一起工作,因为 C 语言规范说:

6.2.6 类型的表示 6.2.6.1 概述1 除本子条款中所述外,所有类型的表示均未指定。 并且它对结构中的默认填充没有任何说明。

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

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


0
投票

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

唯一可能让您失望的是 #pragma 指令,该指令对结构进行了一些非常不寻常的对齐设置。例如,如果双精度数在您的计算机上占用 8 个字节,并且 #pragma 指令要求在 16 字节边界上对齐每个成员,则可能会导致问题。除此之外你都很好。


0
投票

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

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


-3
投票

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

它没有内置于语言中,但我这样做会感到安全。就风格而言,我不会这样做,因为它只是令人困惑。

© www.soinside.com 2019 - 2024. All rights reserved.