使用 reinterpret_cast将数据从 std::vector 重塑<double>为指定维度的双精度**

Reshaping data from a std::vector<double> into double** of specified dimensions using reinterpret_cast

本文关键字:gt reinterpret 双精度 double std 数据 cast vector lt 使用 重塑      更新时间:2023-10-16

我有一个包含M*N值的std::vector<double>,我想将其重塑为一个double**,其行为类似于double[N][M]多维数组。 这些起点和终点可能看起来很奇怪,但不幸的是,它们都是由外部库决定的,所以我对它们无能为力。我问这个问题是为了看看是否有办法完成此操作,而无需简单地手动复制所有数据。

我确实读过这个问题,答案很好,但它的起点略有不同——不是从std::vector<double>double**,而是从double[]double[][]。我试图了解那里发生了什么,并将相同的原则应用于我的情况,但我无法让我的代码工作。如何正确执行此操作?

const int M = 3;
const int N = 2;
std::vector<double> f = { 0, 1, 2, 3, 4, 5 };
double* f1d = f.data();
// compiles and doesn't crash
// but f2d seems to be unitialized (at least f2d[1][1] is garbage)
// also has the wrong type (double[][] rather than double**)
double (&f2d)[N][M] = reinterpret_cast<double (&)[N][M]>(f1d);
// segfaults when trying to access e.g. f2d[0][0]
double** f2d = reinterpret_cast<double**>(f1d);

最终,我需要将数据传递到一个采用double**参数的方法中,因此以下内容必须编译和运行而不会出现问题:

#include <iostream>
#include <vector>
void test(int M, int N, double **arr) {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
};
int main() {
const int M = 3;
const int N = 2;
std::vector<double> f = { 0, 1, 2, 3, 4, 5 };
// whatever I need to do here to get f2d
test(M, N, f2d);
return 0;
}

预期产出:

0 1 2 
3 4 5

基于double**的"数组"与实际的多维数组完全不同,即double[N][M].它具有完全不同的布局并存储不同的信息,因此如果不存储任何其他信息,您就不可能做您想做的事情。

基于double**的"数组"的工作方式是两级结构,其中第一级是包含指向各种常规一维数组的指针的数组。连续的多维数组不能直接用作基于double**的"数组",因为带有指针的第一级结构无处可见:double[N][M]中的各种子数组从基址和大小中是隐含的。

double[3][2]
+----+----+----+----+----+----+
| 00 | 01 | 02 | 10 | 11 | 12 |
+----+----+----+----+----+----+
double**
+------------+------------+
| 0xDEADD00D | 0xDEADBABE |
+------------+------------+
|            |
+-----+            |
|                  |
v                  |
+----+----+----+   |
| 00 | 01 | 02 |   |
+----+----+----+   |
v
+----+----+----+
| 10 | 11 | 12 |
+----+----+----+

所以现在,这已经不碍事了,我们了解了基于double**的"数组"是如何工作的,我们可以开始尝试解决您的问题了。

要获得double**,您需要做的是通过用指向单个连续数组中各种地址的指针填充单独的指针数组来自己提供第一级结构。

std::vector<double*> index;
index.reserve(M);
for(int i = 0; i < M; ++i) {
index.push_back(&f[i*N]);
}
double** f2d = index.data();

这为您提供了最小的麻烦和最小的空间开销。它也不执行任何数据副本,因为我们只收集一堆指向现有存储的指针。

您最终会得到如下所示的布局:

index
+------------+------------+
| 0xDEADD00D | 0xDEADBABE |
+------------+------------+
|            |
+-----+        +---+
|              |
v              v
+----+----+----+----+----+----+
f | 00 | 01 | 02 | 10 | 11 | 12 |
+----+----+----+----+----+----+

显然,您无法从数组中获取有效的double**,因为没有要指向的double*数组。您需要将平面数组重新解释为二维数组,而不是指针数组。

第一次尝试几乎是正确的;但它将对指针f1d的引用重新解释为二维数组,而不是它指向的数组。取消引用指针,并重新解释对数组的结果引用,应该可以工作:

double (&f2d)[N][M] = reinterpret_cast<double (&)[N][M]>(*f1d);
^

或者,您可以重新解释对f[0]的引用,而无需中间指针。

如您所见,使用reinterpret_cast非常容易出错。您可以考虑将向量包装在一个类中,使用访问器函数来执行二维索引所需的算术。

更新:如果出于某种原因你真的想要一个double**而不是二维数组,你需要自己构建一个指针数组,

按照
std::vector<double*> pointers;
for (i = 0; i < M; ++i) {
pointers.push_back(&f[i*N]);
}
double** f2d = pointers.data();