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

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

  • автор:

Как передать двумерный динамический массив в другую функцию?

Доброго времени суток, сегодня я столкнулся с такой проблемой:
Как передать двумерный динамический массив в другую функцию и использовать данный двумерный массив в привычном виде?
То есть использовать как в данном коде:

#include void func(int a[][], int n, int m) < for (int i=0; i> > int main() < const int n = 2; const int m = 3; int a[n][m] = < , , >; func(a, n, m); >

P.S. Код не рабочий. Написал как пример для описания суть проблемы.

  • Вопрос задан более трёх лет назад
  • 2361 просмотр

Решения вопроса 1
Программист на «си с крестами» и не только

Есть два способа наладить динамический 2D-массив: «хребет» и «линейный массив».

Хребет определяется как int** a = new int*[m];
А затем каждый элемент хребта присваиваете new.
Доступ a[i][j].

Линейный массив определяется int* a = new int[m*n];
Доступ a[i*n + j].

Ваш случай — линейный массив, то есть

void func(int a[], int n, int m) … func(a[0], n, m);

Советую как-то заинкапсулировать эти массивы и передавать
void dumpArray(const Array2D& x)
К сожалению, у инкапсулированного массива нет хорошего способа сделать a[i][j], но можно a(i,j).

Передача динамического двумерного массива в функцию

Author24 — интернет-сервис помощи студентам

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

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

Передача двумерного динамического массива в функцию(объяснение)
Когда мы хотим передать двумерный динамический массив в функцию через указатели (**a) , то в теле.

Передача двумерного динамического массива чисел в функцию
void Map::setCell(int** block, Room* room, int type)< for(int i=room->x; i<room->w; ++i).

683 / 232 / 16
Регистрация: 15.10.2007
Сообщений: 1,247
да,но размер не обязательно по ссылке передавать
577 / 571 / 65
Регистрация: 29.01.2009
Сообщений: 1,274

void PrintMatrix(int** matrix, int& n, int& m)

исправь на

void PrintMatrix(int** matrix, int *n, int *m)

Регистрация: 14.04.2009
Сообщений: 448
А в чем разница?
2816 / 1407 / 107
Регистрация: 07.03.2009
Сообщений: 4,446

Gravity, вот исправь и проверь. Если ты меняешь на указатели, то весь вызов и функцию прийдется переписывать.

rar14, рациональней наверное будет сделать так:

1 2 3 4 5 6 7 8 9 10 11 12 13
void PrintMatrix(int** matrix, int n, int m) { for (int i = 0; i  n; i++) { cout  <"Row "  <(i + 1)  <": "; for (int j = 0; j  m; j++) { cout  <"\t"  [i][j]; } cout  <"\n"; } cout  <"\n"; }

Регистрация: 14.04.2009
Сообщений: 448

А можно теоретически объяснить, почему лучше передавать m и n как указатели, а не как ссылки? Или, почему лучше передавать локальными параметрами? а не по ссылке?

577 / 571 / 65
Регистрация: 29.01.2009
Сообщений: 1,274

Monte-Cristo, уже заметил, смутило взятие адреса в параметрах.

rar14, если ты не собираешься присваивать новые значения существующим m и n, то передавай их в функцию по значению, в обратном случае по ссылке.

2816 / 1407 / 107
Регистрация: 07.03.2009
Сообщений: 4,446

существует три вида передачи параметров в функцию:
1. по значению
2. по указателю
3. по ссылке

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include using namespace std; void func1(int x) // передача по значению { x = 1; } void func2(int& x) // перадча по ссылке { x = 2; } void func3(int* x) // передача по указателю { *x = 3; } int main() { int a = 0; func1(a); cout  a  endl; func2(a); cout  a  endl; func3(&a); cout  a  endl; return 0; }

Передача двумерного динамического массива в функцию (Си)

Подскажите как правильно передать двумерный динамический массив в функцию. Функция не возвращает значения (void), массив в ней должен изменяться. При передачи в аргумент просто int **Arr, создается копия и изменяется только копия, мне же нужно чтобы изменялся именно передаваемый через аргументы массив.

 void ArrayInput2(int **X, int n, int m, int **Y, int a, int b) < FILE *file; file = fopen("matrix.txt", "r"); int i, j; X = (int**)malloc(n * sizeof(int*)); for (i = 0; i> Y = (int**)malloc(a * sizeof(int*)); for (i = 0; i > fclose(file); > 

Отслеживать
задан 11 июн 2018 в 15:56
15 1 1 серебряный знак 4 4 бронзовых знака
«При передачи в аргумент просто int **Arr, создается копия» — это сомнительно.
– user176262
11 июн 2018 в 15:57

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

11 июн 2018 в 16:03
Пусть Ваша функция возвращает int ** .
– user176262
11 июн 2018 в 16:04
@Uefa Покажите код.
11 июн 2018 в 16:07

Не получиться, т.к. из файла нужно считать 2 массива. Простейший способ, это объявить функцию void и передать через аргументы обе функции и за 1 вызов функции все записать. Иначе при каждом вызове функции программа записывает в массив данные сначала файла.

11 июн 2018 в 16:10

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Во-первых, «возвращаемые» параметры вам в такой ситуации придется передавать «по указателю». Так как сами «возвращаемые» параметры имеют тип int ** , то при передаче «по указателю» они превратятся в int ***

Во-вторых, чтобы не путаться в «звездочках», выделение памяти я бы порекомендовал делать через следующую идиому

dst = malloc(N * sizeof *dst); 

(и заодно избавиться от манеры явно приводить результат malloc )

void ArrayInput2(int ***pX, int n, int m, int ***pY, int a, int b) < . *pX = malloc(n * sizeof **pX); for (int i = 0; i < n; ++i) < (*pX)[i] = malloc(m * sizeof *(*pX)[i]); for (int j = 0; j < m; ++j) fscanf(file, "%d", &(*pX)[i][j]); >. 

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

void ArrayInput2(int ***pX, int n, int m, int ***pY, int a, int b) < . *pX = malloc(n * sizeof **pX); **pX = malloc(n * m * sizeof ***pX); for (int i = 0; i < n; ++i) < (*pX)[i] = **pX + i * m; for (int j = 0; j < m; ++j) fscanf(file, "%d", &(*pX)[i][j]); >. 

При вызове такой функции, разумеется, надо не забывать передавать именно указатели на указатели-приемники

int **X, **Y; ArrayInput2(&X, 10, 20, &Y, 30, 40); 

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

int **matrix_input(FILE *f, int n, int m) 

Тогда и «звездочек» этих многоэтажных было бы меньше.

Отслеживать
ответ дан 11 июн 2018 в 18:04
AnT stands with Russia AnT stands with Russia
69.3k 3 3 золотых знака 63 63 серебряных знака 140 140 бронзовых знаков

избавиться от манеры явно приводить результат malloc , если не секрет, почему? Чем не угодил такой каст?

11 июн 2018 в 19:38

@NewView: Он много чем не угодил. Во-первых, он нинафиг не нужен. Делать что-то ненужное, а затем спрашивать «чем не угодил» — странное занятие, особенно если речь идет о чем-то настолько костыльно-некрасивом, как касты. Кастов в коде быть не должно вообще, за редким исключением тех случаев, когда они неизбежны.

11 июн 2018 в 19:48

Во-вторых, такое приведение забивает код упоминаниями имен типов. Имена типов должны упоминаться только в объявлениях. Сам код же (statements) должен быть настолько типонезависим, насколько это возможно. Упоминаний имен конкретных типов там быть не должно (опять же: за редким исключением тех случаев, когда они неизбежны). Это пресловутый принцип DRY (don’t repeat yourself): не надо повторять то, что и так уже известно.

11 июн 2018 в 19:49
В Си достаточно двух звёзд, зачем три?!
12 июн 2018 в 2:31
И умножение для подсчёта необходимого размера памяти теоретически может вызвать переполнение целого.
12 июн 2018 в 2:35

А не проще создать структуру, а не городить эти трех этажные звезды

typedef struct matrix_s < int *data; size_t width; size_t height; >matrix_s; 

и функцию для создания

matrix_s *matrix_new(size_t w, size_t h); 

и передавать ее в вашу функцию загрузки из файла, что-то типа

int load_matrix_fromfile(matrix_s *a, matrix_s *b, const char *filename) < FILE *fp = fopen(filename, "r"); if (!fp) return -1; for (size_t i = 0; i < a->width * a->height; i++) fscanf(fp, "%d", &a->data[i]); for (size_t i = 0; i < b->width * b->height; i++) fscanf(fp, "%d", &b->data[i]); fclose(fp); return 1; > 

а для чтения записи матрицы использовать

int *matrix_setget(matrix_s *m, size_t x, size_t y) < return m->data + m->width * y + x; > matrix_s *m = matrix_new(3, 3); *matrix_setget(m, 0, 0) = 15; 

Мне кажется читать код будет точно приятнее и понятней

Отслеживать
ответ дан 11 июн 2018 в 19:41
Norman Bytes Norman Bytes
685 3 3 серебряных знака 8 8 бронзовых знаков

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

11 июн 2018 в 20:25
Ну, я думаю это и так понятно.
11 июн 2018 в 20:31
Мне понятно, ТС и таким же новичкам, зашедшим из гугла — не уверен
11 июн 2018 в 20:59

@avp: А чем это «настоящее» чем исходный вариант? Во-первых, оба варианта являются равноценными способами реализации двумерного массива (матрицы). У каждого из них свои достоинства и недостатки и, соответственно, области применения. Во-вторых, разница между этими двумя вариантами не так уж и велика, если обратить внимание на то, что память второго уровня запросто может быть выделена единым блоком (как в моем ответе).

12 июн 2018 в 1:02

@AnT, тем, что память выделяется так же, как и для матрицы int a[N][M]; и отсутствует вектор указателей на строки (даже если выделить память под все элементы одним блоком (кстати, а зачем в таком случае нужен вектор указателей? Это не будет провоцировать ошибки?)). По поводу достоинств метода с вектором указателей — imho только 1) естественная для программиста индексация элементов (без дополнительных усилий) 2) теоретическая возможность работать в экстремальных условиях фрагментации памяти

Функции в C++ для начинающих Передача в функцию двумерного динамического массива

Иногда возникает вопрос по работе массивов и функций. Многие испытывают проблемы при создании двумерного динамического массива для последующей его обработки с помощью функций. Найти информацию решения таких задач на момент написания статьи не так уж и просто. Да, описаны варианты создания, но лично я находил или простое создание двумерного динамического массива внутри main () или выделение памяти для массива непосредственно внутри принимающей его функции. Мне это очень не нравилось

Код С++ Создание динамического двумерного массива и обработка его с помощью функций
// Отображение массива
void ArrayShow (int ** A ,int N ,int M )
cout
for (int i = 0 ; i < N ; i ++)
for (int j = 0 ; j < M ; j ++)
if (!( j % M )) cout //Чтобы массив выглядел как массив
cout //Табуляция символов
// Инициализация массива случайными значениями
void ArrayInit (int ** A ,int N ,int M )
srand ( time ( 0 ));
for (int i = 0 ; i < N ; i ++)
for (int j = 0 ; j < M ; j ++)
A [ i ][ j ]= rand ()%( 100 )- 50 ; //Случайное значение в очередную ячейку массива
void main ()
clrscr ();
int ** A ; //Для создания двумерного массива удобен указатель на указатель
int N , M ; //Число колонок=N, Число строк= M
cin >> N >> M ; //Ввели размерность массива
// for ( int k=0; k<10;k++) Может кому нужно циклом /* Создание динамического двумерного массива */
A =new int*[ N ];
for (int i = 0 ; i < N ; i ++) A [ i ]=new int[ M ];
ArrayInit ( A , N , M ); //Функция создания массива

ArrayShow ( A , N , M ); //Функция обработки массива
/*Очистка памяти от созданного двумерного массива*/
for ( i = 0 ; i < N ; i ++) delete [] A [ i ];
delete [] A ;
A = NULL ;
//> окончание цикла for

getch ();
return;

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

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

При написании приведенной программы, ячейки для двумерного массива были динамически созданы внутри функции main () Сразу же был дописан код очистки памяти от создаваемого массива. Если написать не сразу, то потом легко запутаться или забыть. Очистка памяти нужна только и только тогда когда массив больше не нужен . Между кусками кода выделения памяти под двумерный массив и очистки от него памяти был написан вызов двух функций. Первая функция принимает двумерный массив в качестве аргумента. Благодаря тому, что принимаемый параметр является указателем, то любые изменения массива внутри функции, влияют на этот массив вне функции напрямую (Передали на обработку -> Получили обновленный) . Нужно заполнить массив значениями. Чтобы заполнить массив значениями, нужно знать его размерность. Чтобы функция занесения данных в двумерный динамический массив знала размерность, туда были переданы эти данные. Осталось только использовать ввод данных с помощью циклов. Не думаю, что по этим циклам должны возникнуть вопросы. Функция маленькая и удобочитаемая. Главное понимать, что

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

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

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

Главное четко осознавать

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

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

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