在C++中将一维数组作为二维数组访问

19

这件事困扰我有一段时间了。很多时候我会创建一个大缓冲区来存储“最大”数量的数据。这有助于避免每次下一个数据集大小变化时动态分配和释放缓冲区。

例如,假设我有一个数组,其实用长度远远超过它实际有用的大小,但我知道有用数据的长度。

int amountOfData = 9;
char data1D[100] = some data that is only 9 bytes long stored in a 100 byte array

假设我有一个算法,我想在这个数据集上运行它,使用 2D 数组索引。所以我希望能够按以下方式访问数据:

cout << "I am accessing this data as a 2D array: " << data1D[0][1] << endl;

假设对于这个算法,我知道2D数组的x和y长度将是:

int xlength = 3;
int ylength = 3;

对于这次迭代,因为 amountOfData = 9。然而下一次迭代可能会有不同的长度。例如,给定 amountOfData = 16,它们可能是 xlength = 4ylength = 4

我想要进行某种类型的转换,允许我使用2D数组索引来访问1D数组。我知道我的初始1D长度是多少,这告诉我我的2D xlengthylength有多长,所以只要最初的100个字节足够容纳任何对我有用的数据集,就可以轻松完成这项工作,而无需使用newmalloc

我意识到:

char** data2d = (char**) data1D;

由于编译器不知道第二个维度的大小,因此这不起作用。但是在运行时,我会知道它是什么!

为什么会出现这种情况的根本原因是什么? 是否有任何解决方法? 我有没有漏掉什么?


1
请查看vectors - Fred Larson
你期望在data1D中使用哪种类型? - Amadeus
@Tomas Badan 我用 char 作为例子,但今天出现了一个特定的问题,当我想要将一个由专有相机软件输出的 unsigned char 的一维数组作为 unsigned short 的二维数组来访问时。我希望能够将结果图像作为2D像素数据进行访问。 - dinkelk
你可以将数组强制转换为char指针,然后将其视为二维数组。请参阅https://dev59.com/L2ct5IYBdhLWcg3wufxb。 - Kevin
4个回答

6

当你只在运行时知道数组的长度时,我认为最好不要使用二维数组来解决这个问题,而是通过使用函数模拟它来解决。例如,在C中:

char data1D[1000] = {0};

unsigned int getElement(unsigned int x, unsigned int y, 
            unsigned int xMax, unsigned int yMax)
{
  // Do some error tests
  return ((unsigned int *) data1D)[x*xMax + y];
}

2
如果您知道行/列长度(取决于行或列主要和其他因素)……我相信它应该是这样的...
char get_value(char *arr, int row_len, int x, int y) {
    return arr[x * row_len + y];
}

针对将一维数组视为二维数组进行处理的方法。

另外还有关于二维动态 C 数组的事情。

char **arr = (char **)malloc(row_size * sizeof(char *));
int x;
for (x = 0; x < row_size; ++x) {
    arr[x] = (char *)malloc(col_size * sizeof(char));
}

我可以将我的列和行混合起来...
像其他人所说的那样,向量很好,因为你正在使用C++:
auto matrix_like_thing = std::vector<std::vector<char> >(rows, std::vector<char>(cols, '\0'));
matrix_like_thing[0][4] = 't';

arr[x * row_len + y]有点不清楚:x是行,y是列,和我期望的相反。通常,在图表上,y是垂直轴,x是水平轴,例如,a[y][x]表示访问第y行和第x列。这个答案更清晰地展示了“将索引为一维数组当作二维数组”的模式:array[row_length * row_index + column_index] - ggorlen

2
原因在于你试图将一个二维数组转换为指向每个指向字符数组的指针的指针数组。
一种选择是创建一些适配器类,使您可以像访问实际的二维数组一样访问数据。这将简化对数组两个方向的访问,并可扩展为与标准库一起使用。
#include <iostream>
#include <sstream>
#include <utility>

template <typename Type, size_t DataSize>
class MDArray
{
public:

    struct SDArray
    {
        SDArray(Type* data, size_t size) : data_(data), size_(size) {}
        SDArray(const SDArray& o) : data_(o.data), size_(o.size_) {}

        size_t size() const { return size_; };

        Type& operator[](size_t index)
        {
            if(index >= size_)
                throw std::out_of_range("Index out of range");

            return data_[index];
        }

        Type operator[](size_t index) const
        {
            if(index >= size_)
                throw std::out_of_range("Index out of range");

            return data_[index];
        }

    private:

        SDArray& operator=(const SDArray&);
        Type* const     data_;
        const size_t    size_;
    };

    MDArray(const Type *data, size_t size, size_t dimX, size_t dimY)
        : dimX_(dimX), dimY_(dimY)
    {
        if(dimX * dimY > DataSize)
            throw std::invalid_argument("array dimensions greater than data size");

        if(dimX * dimY != size)
            throw std::invalid_argument("data size mismatch");

        initdata(data, size);
    }

    size_t size() const { return dimX_; };
    size_t sizeX() const { return dimX_; };
    size_t sizeY() const { return dimY_; };

    SDArray operator[](const size_t &index)
    {
        if(index >= dimY_)
            throw std::out_of_range("Index out of range");

        return SDArray(data_ + (dimY_ * index), dimX_);
    }

    const SDArray operator[](const size_t &index) const
    {
        if(index >= dimY_)
            throw std::out_of_range("Index out of range");

        return SDArray(data_ + (dimY_ * index), dimX_);
    }

private:

    void initdata(const Type* data, size_t size)
    {
        std::copy(data, data + size, data_);
    }
    MDArray(const MDArray&);
    MDArray operator=(const MDArray&);

    Type            data_[DataSize];
    const size_t    dimX_;
    const size_t    dimY_;
};


int main()
{
    char data[] = "123456789";
    MDArray<char, 100> md(data, 9, 3, 3);


    for(size_t y = 0; y < md.sizeY(); y++)
    {
        for(size_t x = 0; x < md.sizeX(); x++)
        {
            std::cout << " " << md[y][x];
        }
        std::cout << std::endl;
    }

    std::cout << "-------" << std::endl;

    for(size_t y = 0; y < md.size(); y++)
    {
        const auto& sd = md[y];
        for(size_t x = 0; x < sd.size(); x++)
        {
            std::cout << " " << sd[x];
        }
        std::cout << std::endl;
    }

    std::cout << "-------" << std::endl;

    for(size_t y = 0; y < md.size(); y++)
    {
        auto sd = md[y];
        for(size_t x = 0; x < sd.size(); x++)
        {
            std::cout << " " << sd[x];
        }
        std::cout << std::endl;
    }
}

@Captian Oblivious 当有用的数据集只有9个时,这个代码还能正常工作吗?当你访问data2d时,它似乎会将其视为100x100的数组,而不是3x3的数组... - dinkelk
啊,不会的。我已经更新了我的答案,提供了另一种解决方案。 - Captain Obvlious

1
如果您正在使用C ++,您可以构建一个简单的包装器来简化访问,例如:
template <typename T>
class A2D {
    T *m_buf;
    size_t m_n;
    size_t m_m;
public:
    A2D(T *buf, const size_t &n, const size_t &m)
        : m_buf(buf), m_n(n), m_m(m) { }
    ~A2D() { }

    T& operator()(const size_t &i, const size_t &j)
    {
        return *(this->m_buf + i * this->m_m + j);
    }
};

使用方法:

int main()
{
    int *a = new int[16];
    for ( int i = 0; i < 16; ++i ) {
        a[i] = i;
    }
    A2D<int> b(a, 4, 4);

    for ( int i = 0; i < 4; ++i ) {
        for ( int j = 0; j < 4; ++j ) {
            std::cout << b(i, j) << ' ';
        }
        std::cout << '\n';
    }
}

使用C语言,您可以通过过程或宏来完成类似的事情。重要的是,不要忘记控制预分配的内存(1D数组)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接