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

问题描述 投票:2回答:4

(这不是引用固定大小的thisthis的重复,问题不是要理解指针是如何存储的,而是编译器可以自动化手动功能)。

基于this SO question,多维数组按顺序存储。

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

但是我试图在预先分配的内存中创建一个2维浮点数组:

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

然后我在尝试:

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

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

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

目前,我是手动完成的,例如:

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;
}

非常感谢。

c++ arrays
4个回答
3
投票

要将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;
}

2
投票

您的代码调用未定义的行为,因为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;
}

live example here

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

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;
}

live example here


1
投票

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

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

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

char c = ar[2][3];

0
投票

对于任何想要实现相同目标的人,我已经创建了一个variadit模板函数,可以在现有内存中创建一个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 char数组:

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++;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.