Как передать матрицу в функцию c
Перейти к содержимому

Как передать матрицу в функцию c

  • автор:

Как передать матрицу в функцию c

БлогNot. C++: как передать в функцию статическую и динамическую матрицу одним и тем же ко.

C++: как передать в функцию статическую и динамическую матрицу одним и тем же кодом?

Возможно, мы могли бы сделать такое в «чистом» C (из-за иного кастинга типов), но не в C++. Функция в наших примерах пусть занимается просто выводом матрицы в консоль.

Вариант 1 — применяем указатели вида тип **матрица . Удобно для динамических матриц, но не для статических.

Обратите внимание на закомментированную строчку

в листинге. Явное приведение типа вида (double **)&a[0][0] откомпилируется, но создаст ошибку времени исполнения.

Вариант 2. Использовать шаблоны, если ваши массивы — статические, и вы хотите одним кодом печатать матрицы разных размерностей.

Но в этом случае так просто не напечатать динамическую матрицу.

Попытаться «помучить» так динамическую матрицу можно, но работать это не будет.

Например, добавим перед cin.get(); такой код:

Можно передать в функцию и

— тоже не поможет 🙂

Дело в том, что типизированность C++ никто не отменял и массив указателей — не то же самое, что массив массивов.

компилятор, фактически, создаёт массив массивов. Да, в памяти это будет

но, благодаря типизированности языка, компилятор запоминает, что тип m — это double[3][4] . В частности, фиксируются размеры 3 и 4 .

Когда Вы пишете m[i][j] , компилятор заменяет это на _m[i * 4 + j] ( 4 берётся из второй размерности в double[3][4] ). Например, m[1][2] == 1 и _m[1 * 4 + 2] == _m[6] == 1 .

Двойной указатель ** — это другой тип, который не несёт информации о размерностях. Чтобы рассматривать double **a как матрицу a[3][4] , a[0] , a[1] и a[2] нужно сделать указателями на double (то есть, они будут иметь тип double * ), указывающими на первый элемент соответствующей строки.

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

Простое приведение типа не создаст этих указателей.

Ну и «безразмерного» типа [][] тоже не существует, хотя можно фиксировать вторую размерность и создать указатель на будущие строки матрицы одинакового размера:

(добавить в вариант 1 кода перед cin.get() ).

Вариант 3. Оптимальное, на мой взгляд, решение — эмулируем матрицу через одномерный массив.

Теперь функция работает одинаково для статики и динамики плюс может работать и с «просто одномерными» массивами, такими как d .

Если не хочется каждый раз вычислять a[i * m + j] вместо a[i] [j], можно обернуть код в класс и переопределить в этом классе оператор [][].

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

Все коды из заметки проверялись в консолях актуальных сборок Visual Studio 2019 и QT 5.X.

Как передать матрицу в функцию c

Использование массива как параметра функции не вызывает ни каких трудностей. Как было сказано ранее, массив в подпрограмму всегда передаётся по адресу, поэтому достаточно при вызове функции указать адрес начала массива или адрес того элемента, начиная с которого предполагается обрабатывать массив.

Пример . Написать программу, которая будет вызывать функцию для вывода на печать элементов массива.

Возможный вариант решения:

using namespace std;

void print(int x[], int n);

void print(int x[], int n)

for(int i = 0; i < n; i++)

При вызове функции оператором

в неё передаётся адрес начала массива x и количество элементов n . Количество элементов n передаётся по значению и здесь всё должно быть понятно, но вот с передачей самого массива может быть некоторое непонимание, которое необходимо устранить.

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

С учётом того, что массив в подпрограмму чаще всего передаётся весь и с начала, разработчики языка запись вида &x[0] сократили до x , т.е. обращение к массиву по имени эквивалентно обращению по адресу к элементу с индексом 0 .

В заголовке функции

void print(int x[], int n) // 1-й вариант

два формальных параметра. Запись x[] говорит о том, что в подпрограмму передаётся именно массив.

Заголовок может выглядеть и так:

void print(int *x, int n) // 2-й вариант

Массив передаётся по адресу, поэтому и записываем первый параметр как адрес на объект типа int . Этот объект-указатель принимает адрес того элемента массива, который вычисляется в вызывающей функции (например, в main() ).

Какую форму использовать? Это дело вкуса. Компилятор рассматривает 1-й вариант как эквивалент 2-го, хотя для человека нагляднее именно 1-й вариант. По нему видно, что формальный параметр — это именно массив, а не указатель, например, на одиночный элемент.

Трактовка при вызове x как &x[0] позволяет при необходимости передать в подпрограмму не весь массив, а только его часть, начиная с какого-либо адреса. Например, вызов

приводит к выводу на экран монитора трёх элементов массива, начиная с элемента с индексом 2 . При этом сама функция не требует каких либо доработок. В этом плане языки С/С++ гораздо удобнее многих других языков. Так, в Паскале или Бейсике пришлось бы передавать в подпрограмму ещё один параметр — номер элемента, с которого необходимо начать обработку массива, а затем этот номер использовать в операторе цикла.

Матрица как параметр функции

Передача в подпрограмму матрицы в качестве параметра несколько сложнее, чем передача одномерного массива.

Матрица также передаётся по адресу. При вызове функции достаточно указать имя матрицы (это будет адресом начала двумерного массива), но как задать описание матрицы в списке формальных параметров? Матрица, как было сказано ранее, — это массив из массивов. Поэтому можно попробовать записать формальный параметр так:

К сожалению, это работать не будет. Первая пара пустых квадратных скобок указывает на то, что имеем дело с массивом. Это допустимо. Вторая пара квадратных скобок как признак того, что это тоже массив, сгенерирует ошибку. Здесь компилятор захочет больше конкретики. Массив из неизвестно каких массивов его не устроит. Во вторых скобках надо записать константу, чтобы компилятор мог понять, как память в матрице распределяется по строкам и столбцам.

Таким образом, допустима запись

Рассмотрим небольшой пример, поясняющий сказанное выше.

Пример . Написать и протестировать функцию вывода значений целочисленной матрицы на экран монитора.

Передать двумерный статический массив в функцию

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

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

Вот демонстрационная программа

Ее вывод на консоль

Если же писать программу на C, а не на C++, то там есть массивы переменной длины, и поэтому вам было бы проще.

В C++ для этих целей лучше использовать стандартный класс std::vector

Что касается вашего вопроса

2)Статические 2D массивы хранятся в памяти линейно. Динамические хранятся тоже линейно или нет?

то если вы будете распределять его в памяти как двумерный массив, то естественно его элементы будут храниться линейно по строкам. Например

А что касается этого вопроса

3)Какую связь имеет статический 2D массив с указателями?

то любой массив в выражениях (за редким исключением) неявно преобразуется в указатель на свой первый элемент.

В gcc сделать это совсем просто.

Вы можете передавать в функцию размерности матрицы (достаточно только «низших») и обращаться к элементам по индексам естестственным образом. Например:

К сожалению g++ (и c++) не поддерживает такую передачу матриц в функцию. Поэтому проще всего (обладая знанием, что все элементы матрицы располагаются в памяти последовательно, сначала первая строка, за ней вторая и т.д.) передать в функцию адрес первого элемента матрицы, а в функции рассматривать ее как одномерный массив.
Естественно, пересчитывать индексы придется самому.

Вот пример файла, в котором представлены обе версии программы и результаты его запуска:

Если вопросы еще остались, задавайте.

UPD

Вот вариант с указателями на строки «плотной» матрицы.

Берем статическую матрицу, делаем для нее массив указателей, передаем его в функцию транспонирования, которая делает в динамической памяти «плотную» транспонированную матрицу и возвращает новый массив указателей на ее строки.

Передать двумерный статический массив в функцию

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

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

Вот демонстрационная программа

Ее вывод на консоль

Если же писать программу на C, а не на C++, то там есть массивы переменной длины, и поэтому вам было бы проще.

В C++ для этих целей лучше использовать стандартный класс std::vector

Что касается вашего вопроса

2)Статические 2D массивы хранятся в памяти линейно. Динамические хранятся тоже линейно или нет?

то если вы будете распределять его в памяти как двумерный массив, то естественно его элементы будут храниться линейно по строкам. Например

А что касается этого вопроса

3)Какую связь имеет статический 2D массив с указателями?

то любой массив в выражениях (за редким исключением) неявно преобразуется в указатель на свой первый элемент.

В gcc сделать это совсем просто.

Вы можете передавать в функцию размерности матрицы (достаточно только «низших») и обращаться к элементам по индексам естестственным образом. Например:

К сожалению g++ (и c++) не поддерживает такую передачу матриц в функцию. Поэтому проще всего (обладая знанием, что все элементы матрицы располагаются в памяти последовательно, сначала первая строка, за ней вторая и т.д.) передать в функцию адрес первого элемента матрицы, а в функции рассматривать ее как одномерный массив.
Естественно, пересчитывать индексы придется самому.

Вот пример файла, в котором представлены обе версии программы и результаты его запуска:

Если вопросы еще остались, задавайте.

UPD

Вот вариант с указателями на строки «плотной» матрицы.

Берем статическую матрицу, делаем для нее массив указателей, передаем его в функцию транспонирования, которая делает в динамической памяти «плотную» транспонированную матрицу и возвращает новый массив указателей на ее строки.

Ну, память перед выходом Вы уж сами освободите?

кроме передачи значений самого массива получаем его размеры через aRows и aCols.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *