C++ 现有内存中的多维数组

C++ Multidimensional array in existing memory

本文关键字:数组 内存 C++      更新时间:2023-10-16

(这不是这个或这个的重复,指的是固定大小,问题不是了解指针是如何存储的,而是编译器是否可以自动执行手动功能)。

基于这个SO问题,多维数组是按顺序存储的。

// These arrays are the same
int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}}; 
int array2[6] = { 0, 1, 2, 3, 4, 5 }; 

但是,我正在尝试在预分配的内存中创建浮点数的二维数组:

float a[5][10] 
float b[50]; // should be same memory

然后我正在尝试:

vector<char> x(1000);
float** a = (float**)x.data();
a[0][1] = 5;

上面的代码崩溃了,显然是因为编译器不知道数组的大小以将其分配给内存中,就像第一个示例中的编译器级已知数组一样。

有没有办法告诉编译器在顺序内存中分配一个多维数组,而无需手动计算指针(例如,通过手动移动索引并调用放置 new)?

目前,我正在手动执行此操作,例如:

template <typename T> size_t CreateBuffersInMemory(char* p,int n,int BufferSize)
{
// ib = T** to store the data
int ty = sizeof(T);
int ReqArraysBytes = n * sizeof(void*);
int ReqT = ReqArraysBytes * (ty*BufferSize);
if (!p)
return ReqT;
memset(p, 0, ReqT);
ib = (T**)p;
p += n * sizeof(void*);
for (int i = 0; i < n; i++)
{
ib[i] = (T*)p;
p += ty*BufferSize;
}
return ReqT;
}

多谢。

要将数组T[rows][cols]分配为一维数组分配T[rows * cols].

要访问该一维数组的元素[i][j],您可以执行p[i * cols + j]

例:

template<class T>
struct Array2d {
T* elements_;
unsigned columns_;
Array2d(unsigned rows, unsigned columns)
: elements_(new T[rows * columns]{}) // Allocate and value-initialize.
, columns_(columns)
{}
T* operator[](unsigned row) {
return elements_ + row * columns_;
}
// TODO: Implement the special member functions.
};
int main() {
Array2d<int> a(5, 10);
a[3][1] = 0;
}

您的代码调用未定义的行为x.data()因为它不指向指针数组,而是指向 1000 个类型为char的对象的数组。你应该感谢它崩溃了... ;-)

访问某种类型的连续缓冲区(就好像它是多维数组一样)的一种方法是将表示多维视图的另一个对象放入此缓冲区中。然后,该视图对象可以提供成员函数以使用多维索引访问数据。要启用a[i][j][k]类型的语法(您似乎正在瞄准),请提供一个重载的[]运算符,该运算符返回一个代理对象,该对象本身提供operator []依此类推,直到您进入单个维度。

例如,对于维度在编译时是固定的,我们可以定义

template <int Extent, int... Extents>
struct row_major_layout;
template <int Extent>
struct row_major_layout<Extent>
{
template <typename T>
static auto view(T* data) { return data; }
};
template <int Extent, int... Extents>
struct row_major_layout
{
static constexpr int stride = (Extents * ... * 1);
template <typename T>
class span
{
T* data;
public:
span(T* data) : data(data) {}
auto operator[](std::size_t i) const
{
return row_major_layout<Extents...>::view(data + i * stride);
}
};
template <typename T>
static auto view(T* data) { return span<T>(data); }
};

然后简单地创建和访问这样的row_major_layout视图

void test()
{
constexpr int M = 7, N = 2, K = 5;
std::vector<int> bla(row_major_layout<M, N, K>::size);
auto a3d = row_major_layout<M, N, K>::view(data(bla));
a3d[2][1][3] = 42;
}

现场示例在这里

或者,如果数组边界是动态的:

template <int D>
class row_major_layout;
template <>
class row_major_layout<1>
{
public:
row_major_layout(std::size_t extent) {}
static constexpr std::size_t size(std::size_t extent)
{
return extent;
}
template <typename T>
friend auto view(T* data, const row_major_layout&)
{
return data;
}
};
template <int D>
class row_major_layout : row_major_layout<D - 1>
{
std::size_t stride;
public:
template <typename... Dim>
row_major_layout(std::size_t extent, Dim&&... extents)
: row_major_layout<D - 1>(std::forward<Dim>(extents)...), stride((extents * ... * 1))
{
}
template <typename... Dim>
static constexpr std::size_t size(std::size_t extent, Dim&&... extents)
{
return extent * row_major_layout<D - 1>::size(std::forward<Dim>(extents)...);
}
template <typename T>
class span
{
T* data;
std::size_t stride;
const row_major_layout<D - 1>& layout;
public:
span(T* data, std::size_t stride, const row_major_layout<D - 1>& layout)
: data(data), stride(stride), layout(layout)
{
}
auto operator[](std::size_t i) const
{
return view(data + i * stride, layout);
}
};
template <typename T>
friend auto view(T* data, const row_major_layout& layout)
{
return span<T>(data, layout.stride, layout);
}
};

void test(int M, int N, int K)
{
std::vector<int> bla(row_major_layout<3>::size(M, N, K));
auto a3d = view(data(bla), row_major_layout<3>(M, N, K));
a3d[2][1][3] = 42;
}

现场示例在这里

基于这个答案,假设你想要一个char数组,你可以做类似的事情

std::vector<char> x(1000);
char (&ar)[200][5] = *reinterpret_cast<char (*)[200][5]>(x.data());

然后你可以使用ar作为普通的二维数组,比如

char c = ar[2][3];

对于任何试图实现相同目标的人,我创建了一个可变模板函数,该函数将在现有内存中创建一个 n 维数组:

template <typename T = char> size_t CreateArrayAtMemory(void*, size_t bs)
{
return bs*sizeof(T);
}
template <typename T = char,typename ... Args>
size_t CreateArrayAtMemory(void* p, size_t bs, Args ... args)
{
size_t R = 0;
size_t PS = sizeof(void*);
char* P = (char*)p;
char* P0 = (char*)p;
size_t BytesForAllPointers = bs*PS;
R = BytesForAllPointers;
char* pos = P0 + BytesForAllPointers;
for (size_t i = 0; i < bs; i++)
{
char** pp = (char**)P;
if (p)
*pp = pos;
size_t RLD = CreateArrayAtMemory<T>(p ? pos : nullptr, args ...);
P += PS;
R += RLD;
pos += RLD;
}
return R;
}

用法:

创建一个 2x3x4 字符数组:

int j = 0;
size_t n3 = CreateArrayAtMemory<char>(nullptr,2,3,4);
std::vector<char> a3(n3);
char*** f3 = (char***)a3.data();
CreateArrayAtMemory<char>(f3,2,3,4);
for (int i1 = 0; i1 < 2; i1++)
{
for (int i2 = 0; i2 < 3; i2++)
{
for (int i3 = 0; i3 < 4; i3++)
{
f3[i1][i2][i3] = j++;
}
}
}