Доступ к массиву 1D как 2D-массив в С++
Это беспокоило меня некоторое время. Много раз я нахожу, что делаю большой буфер для хранения "максимального" количества данных. Это помогает мне избежать динамического выделения и освобождения буфера каждый раз, когда изменяется размер следующего набора данных.
Например, у меня есть массив, который слишком велик для его фактического полезного размера, но я знаю длину полезных данных.
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;
Скажем, для этого алгоритма я знаю, что длина x и длина двумерного массива будут:
int xlength = 3;
int ylength = 3;
для этой итерации, потому что amountOfData = 9
. Однако длины для другой итерации могут быть разными. То есть. они могут быть xlength = 4
и ylength = 4
заданы amountOfData = 16
.
Я хочу сделать какое-то литье, которое позволяет мне использовать 1D-массив, используя двумерную индексацию. Я знаю, как долго моя начальная 1D-длина, что говорит мне, как долго мои 2D xlength
и ylength
, поэтому это должно быть легко обойтись без использования new
или malloc
, пока начальные 100 байтов достаточно долго, чтобы хранить полезные данные для меня.
Я понимаю, что:
char** data2d = (char**) data1D;
не будет работать, потому что компилятор не знает размер второго измерения. Но я буду знать, что это такое во время выполнения!
В чем причина этого, так оно и есть?
Есть ли обходные пути?
Я что-то пропустил?
Ответы
Ответ 1
Как только вы знаете длину своего массива во время выполнения, я думаю, что лучше решить эту проблему, не используя 2D-массив, а подражая ему с помощью функций. Например, в 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
Причина, по которой актер не работает, заключается в том, что вы пытаетесь преобразовать 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;
}
}
Ответ 3
Если вы знаете свою длину строки/столбца (в зависимости от основной строки или столбца, а что нет)... Я считаю, что это что-то вроде...
char get_value(char *arr, int row_len, int x, int y) {
return arr[x * row_len + y];
}
... для обработки 1D-массива как 2D.
Другое дело для 2D динамических массивов 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));
}
Я мог бы смешивать свои столбцы и строки...
Как и все остальные, векторы хороши, поскольку вы используете С++:
auto matrix_like_thing = std::vector<std::vector<char> >(rows, std::vector<char>(cols, '\0'));
matrix_like_thing[0][4] = 't';
Ответ 4
Если вы используете С++, вы можете создать простую оболочку для упрощения доступа, например:
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)