Привести (C как) указатель на матрицу C ++ - PullRequest
0 голосов
/ 08 июня 2018

Я хотел бы передать матрицу из одной функции в другую, поэтому я сделал:

#include <cstdlib>
using namespace std;

const int N = 2;

void init_matrix(int (*matrix)[N]) 
{
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}

int main() 
{
    int *x = (int*)malloc(N*N*sizeof(int));
    init_matrix( (int (*)[N]) x );
    return 0;
}

Как мне сделать вызов на init_matrix() проще?Я представляю это как init_matrix(x, N).

Обратите внимание, что я как бы избегаю std::vector, потому что вскоре я поиграю с параллельным программированием (pthreads, OpenMP, MPI).Поэтому я попрошу решение без std подходов для построения матрицы.

Ответы [ 5 ]

0 голосов
/ 08 июня 2018

Нет ничего плохого в использовании std::vector для многопоточного программирования, например, объяснено здесь .Любое другое хранилище столь же опасно.

Помимо всех векторных примеров, показанных в других ответах: Если вы используете матрицы фиксированного размера, вы можете даже подумать о переходе к шаблонам на основе std::array.

#include <iostream>
#include <array>

template <class T, size_t rows, size_t cols>
class Matrix {
public:
    Matrix() {}
    Matrix(T init) { data.fill(init); }
    double& operator() (size_t row, size_t col)
    {
        if (row < rows && col < cols) return data[cols*row + col];
        throw std::out_of_range("Matrix subscript out of bounds.");
    }
    double operator() (size_t row, size_t col) const
    {
        return data(row, col);
    }
private:
    std::array<T, rows*cols> data;
};

int main()
{
    Matrix<double, 2, 2> mat(1.0);
    mat(1,1) = 2.3;
    std::cout << "mat(1,1) = " << mat(1,1) << std::endl;
}

ps В примере реализована проверка неверного размера.

0 голосов
/ 08 июня 2018

Вы можете использовать псевдоним, чтобы звонок выглядел лучше:

#include <cstdlib>
#include <iostream>
using namespace std;

const int N = 2;

using TypeMatrix =int (*)[N]; //alias

void init_matrix(TypeMatrix matrix) { //simply use the alias
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}


int main() {
    TypeMatrix x = (TypeMatrix)(int*)malloc(N*N*sizeof(int)); //avoid malloc
    init_matrix(x);
   }

Обратите внимание, что вы можете перевести его на следующий уровень, используя псевдоним шаблона, например:

#include <cstdlib>
#include <iostream>
using namespace std;

const int N = 2;

template<typename T , int K>
using TypeMatrix =T (*)[K];

using MatrixInt =TypeMatrix<int,N>;
using MatrixDouble =TypeMatrix<double,N>;


template <typename Matrix>
void init_matrix(Matrix matrix) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1.1;
}


template <typename Matrix>
void print(Matrix matrix) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            std::cout<<matrix[i][j]<< " ";
}


int main() {
    MatrixInt x = (MatrixInt) (new int[N*N]);
    MatrixDouble y = (MatrixDouble) (new double[N*N]);
    init_matrix(x);
    init_matrix(y);
    print(x);
    print(y);
}

При этом я настоятельно не рекомендую использовать необработанные указатели и смешивать C и C ++ (malloc против new) для такой работы.Вместо этого используйте std::array или std::vector.

0 голосов
/ 08 июня 2018

Несмотря на плохую практику использования malloc в C ++, затем ...

Вы можете заменить указатель массива в параметре функции на массив.Это вернется к указателю массива, но это сделает интерфейс функции немного более понятным.

Вы не можете конвертировать между int(*)[N] и (int*) ни в C, ни в C ++.Они не являются совместимыми типами указателей.Даже если они указывают на один и тот же адрес, вы получите неопределенные проблемы поведения, вызванные строгим псевдонимом указателя.

Вы могли бы переписать код следующим образом, но это также не рекомендуется:

#include <cstdlib>
using namespace std;

const int N = 2;

void init_matrix(int matrix[N][N]) {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            matrix[i][j] = 1;
}

int main() {
    int (*x)[N] = (int(*)[N])malloc(N*N*sizeof(int));
    init_matrix(*x);
}

Я бы не рекомендовал его в C ++ - этот стиль довольно громоздок без функции VLA в C. В C ++, вероятно, разумнее использовать std::array&.

0 голосов
/ 08 июня 2018

Только не надо.

Многомерный массив - это не то место, где C ++ в лучшем виде.Здесь это все еще приемлемо, потому что N является постоянной времени компиляции, но если бы это была переменная времени выполнения, вы бы отключились, потому что VLA не поддерживаются в стандарте C ++ (даже если они поддерживаются как расширение реализации некоторыми реализациями, такими как gcc и clang).

Так что, если вам действительно нужно иметь дело с настоящими двумерными массивами, просто используйте пользовательский класс, содержащий базовый std::array для фиксированного измерения времени компиляции или вектор для измерений времени выполнения дляобщий размер и предоставить 2d доступа к этому.Поскольку std::arrays и векторы являются объектами, вы избегаете копирования и перемещения ресурсов (*).

Упрощенная реализация может быть такой:

class matrix {
   std::vector<int> vec;
   int  rows;
   int cols;

public:
   matrix(int i, int j): rows(i), cols(j), vec(i * j) {}

   int& at(int i, int j) {
       return vec[j + i * cols];
   }

   const int& at(int i, int j) const {
       return vec[j + i * cols];
   }
};

Таким образом, базовая структура все ещеистинный 2D-массив, и у вас есть методы для его использования с двумя измерениями

Чего здесь еще не хватает:

  • индексы и размеры, вероятно, должны быть size_t вместо int
  • индексы могут быть проверены на допустимый диапазон в at методах - если вы их не проверяете, функция не должна вызываться at ...
  • otherконструкторы для построения матрицы поверх существующего двумерного массива (который может быть получен, например, из унаследованного кода C)

Дополнительное замечание:

Вы говорите, что хотите избежать векторов длявозможна многопоточность.Во-первых, я не могу себе представить, почему вектор был бы менее многопоточным, чем динамический массив, выделенный вручную.Во-вторых, если вам действительно нужно ручное выделение, вам нужно будет следовать правилу трех / пяти и реализовать собственные конструкторы копирования / перемещения и оператор присваивания в дополнение к пользовательскому деструктору.


(*) У меня естьпроект для написания универсального мульти-D контейнера, поддерживающего operator [] и итераторов, поэтому я знаю, что это довольно сложная задача.Я уже отправил предварительную версию для обзора кода , но она все еще далека от простого использования.

0 голосов
/ 08 июня 2018

Если вам по какой-то причине требуется динамическое выделение смежных целых чисел, вы можете использовать вектор массивов или умный указатель.

std::vector<int[N]> x(N);
std::unique_ptr<int[][N]> x = std::make_unique<int[][N]>(N);

Используя вашу реализацию init_matrix, вы можете просто передатьадрес первого элемента.

init_matrix(&x[0]);

Однако было бы предпочтительно передать вектор или умный указатель по ссылке.

void init_matrix (std::vector<int[N]> &matrix) {
    ...
}

void init_matrix (std::unique_ptr<int[][N]> &matrix) {
    ...
}

...
init_matrix(x);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...