Передача двумерного динамического массива в функцию (Си)
Подскажите как правильно передать двумерный динамический массив в функцию. Функция не возвращает значения (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
69k 3 3 золотых знака 62 62 серебряных знака 139 139 бронзовых знаков
избавиться от манеры явно приводить результат 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) теоретическая возможность работать в экстремальных условиях фрагментации памяти
Динамические двумерные массивы
Динамические двумерные массивы в языке Си имеют сложный способ представления в памяти компьютера.
Рассмотрим одномерный массив из 10 указателей на объекты типа int:
A представляет собой указатель на указатель на int.
Кроме того, массив указателей может быть не статическим, а динамическим:
Следующий шаг сделать очень просто — по указателям, хранящимся в массиве A могут лежать не по одному значению, а по одномерному динамическому массиву таких значений.

Передача динамических двумерных массивов в функцию
Динамические массивы передаются в функции по-другому, передается указатель на начало массива указателей, а длина строки и количество строк вообще нигде не фигурируют. Контроль за границами массивов лежит полностью на программисте, поэтому, вероятно, стоит передавать в функцию отдельными параметрами размеры массива — количество строк и столбцов.
Пример работы с динамическим двумерным массивом
#include
#include
#define MATRIX_HEIGHT 4
#define MATRIX_WIDTH 5
void dynamic_array_print(int **A, size_t N, size_t M)
for(int i = 0; i < N; i++) for(int j = 0; j < M; j++) printf("%*d", 5, A[i][j]);
>
printf(«\n»);
>
>
/*
return pointer on 2d dynamic array
!allocates memory -> to be freed later
*/
int ** dynamic_array_alloc(size_t N, size_t M)
int **A = (int **)malloc(N*sizeof(int *));
for(int i = 0; i < N; i++) A[i] = (int *)malloc(M*sizeof(int));
>
return A;
>
void dynamic_array_free(int **A, size_t N)
for(int i = 0; i < N; i++) free(A[i]);
>
free(A);
>
void dynamic_array_test(size_t N, size_t M)
int **A = dynamic_array_alloc(N, M);
int x = 1;
for(int i = 0; i < N; i++) for(int j = 0; j < M; j++) A[i][j] = x;
x += 1;
>
>
dynamic_array_print(A, N, M);
/*memory investigation*/
printf(«\n Pointers to lines: «);
for(int **p = A; p < A + 3; p++)
printf(«%10d», (long int)*p);
printf(«\n Direct memory access (dangerous. ):\n»);
for(int *p = (int*)*A; p < (int*)*A + 25; p++)
printf(«%d\t», *p);
dynamic_array_free(A, N);
>
int main()
dynamic_array_test(MATRIX_HEIGHT, MATRIX_WIDTH);
return 0;
>
Выделение памяти под динамический массив
Как видно из примера, создание такой сложной структуры как двумерный динамический массив требует множества системных вызовов по выделению памяти:
int **A = (int **)malloc(N*sizeof(int *));
for(int i = 0; i < N; i++) A[i] = (int *)malloc(M*sizeof(int));
>
При таком выделении памяти нельзя просто взять, и освободить память по адресу A, т.к. будет возникать утечка памяти.
Правильное очищение таково:
for(int i = 0; i < N; i++) <
free(A[i]);
>
free(A);
Альтернатива такова: при некотором владении адресной арифметикой можно выделить память сразу для всех одномерных массивов, необходимых для организации двумерного динамического массива:
int ** A = malloc(n*sizeof(int*) + n*m*sizeof(int));
char * pc = A;
pc += n*sizeof(int*);
for (int i=0; i A[i] = pc + i*sizeof(m*sizeof(int));
Тогда освобождение памяти будет происходить очень легко:
Функции в 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 () Сразу же был дописан код очистки памяти от создаваемого массива. Если написать не сразу, то потом легко запутаться или забыть. Очистка памяти нужна только и только тогда когда массив больше не нужен . Между кусками кода выделения памяти под двумерный массив и очистки от него памяти был написан вызов двух функций. Первая функция принимает двумерный массив в качестве аргумента. Благодаря тому, что принимаемый параметр является указателем, то любые изменения массива внутри функции, влияют на этот массив вне функции напрямую (Передали на обработку -> Получили обновленный) . Нужно заполнить массив значениями. Чтобы заполнить массив значениями, нужно знать его размерность. Чтобы функция занесения данных в двумерный динамический массив знала размерность, туда были переданы эти данные. Осталось только использовать ввод данных с помощью циклов. Не думаю, что по этим циклам должны возникнуть вопросы. Функция маленькая и удобочитаемая. Главное понимать, что
Передавая двумерный массив, в функции можно принять указатель на указатель массива и указать число строк и колонок внутри массива .
Следующая функция принимает динамический массив как обычный двумерный. Чтобы массив принялся в таком виде, в котором был только что создан, обязательно принимающую его функцию вызывать до очистки памяти от нужного массива. По принимаемым параметрам эта функция похожа на ту, которая создавала двумерный массив. Используется указатель на указатель, и указываются данные размерности. Чтобы массив был не в одну строчку, была произведена проверка деления числа нацело. Если кто-то использует другие способы приведения массива к хорошему виду, то легко можно использовать удобные ему. Чтобы элементы массива не слились воедино, был использован элемент табуляции.
В качестве эксперимента была попытка реализовать создание динамического двумерного массива внутри цикла, потом передать этот массив в функции. Этот момент комментирован, но может оказаться кому-то полезным.
Главное четко осознавать
- если выделили память, то обязательно нужно её удалять.
- Удалять память нужно только и только тогда, когда динамически созданный массив больше реально не нужен
- Для передачи двумерного динамического массива в функцию удобно использовать указатель на указатель и при передаче его в функцию, в функцию стоит передавать размерность массива
Как передать двумерный динамический массив в функцию c
Как сделать передачу двумерного массива через параметр функции?
Либо создаёшь массив динамически:
Тогда можно написать функцию:
и использовать её прямо: f(arr);
Если же твой двумерный массив статический, то тебе обязательно нужно в объявлении функции указать одну (вторую) размерность:
Вот она, моя функция:
float det(float p[][], int n)
else add[х-1][y-1] = p[y][х];
det += p[i][0]*det(add, n-1))
Попытался перевести на указатели, получилось вот что:
float det(float **p, int n)
for(int i=0; int m=n>i; i++)
float **add = new float*[n-1];
else add[х-1][y-1] = p[y][х];
static float det;
det += p[i][0]*det(add, n-1); // вот здесь ошибка, не хочет вызываться [C++ Error] Call of nonfunction
А ещё можно ли как-нибудь за счёт параметра n указать сразу в параметре массива его размерность типа (n, m[n][n]).
void func(char mas[10][5]);
Ну, какие есть замечания:
1) Зачем тебе две переменных det — одна static, другая auto ??
2) Почему переменная det называется так же, как и функция? Или в латинском алфавите букв мало 😕 Тогда используй знак подчёркивания и цифры (цифры — не в начале). Компилятор, собственно, на это и ругается.
3) Если ты выделил память под переменную с помощью оператора new, то, когда она больше не нужна, освободи ресурс оператором delete. Пример
int* pa = new int;
Используй оператор delete[] для освобождения памяти для целого массива. Для двумерного массива это будет выглядеть так:
float **add = new float*[n-1];
add[i] = new float[n-1];
- Стандарт языка программирования Си
- Передача параметра в функцию по указателю в C стиле
- Передача параметров в функцию по указателю (C стиль) и по ссылке (C++ стиль)
- Передача структуры в функцию и изменение значений элементов структуры
- В чем разница объявления строки как массива и как указателя
- Использование очень больших чисел
- getch() в Linux
- Аргументы функции main()
- Как побайтно считать файл
- Реализация циклического сдвига ROR и ROL
- Динамическая загрузка библиотек в Linux
- Функция fmemopen() — открытие набора байт как файла в памяти с получением файлового дескриптора
- Подмена дефайнов (#define)
- Успешной отладки, шутка
- Как описывать функции с аргументами по-умолчанию в C/C++
- Форматированный вывод через функцию printf
- Создание и удаления двухмерного и трехмерного динамического массива
- Как передать в функцию двумерный массив, размер которого известен
- Как передать в функцию динамический двумерный массив
- Как правильно читать объявления в Си
- Вычисление pbkdf2 на языке C
- Определение разрядности платформы 32 или 64 бит
- Функции getuid() и geteuid()
- Указатели и символьные строки в языке C
- Практическое применение LD_PRELOAD или замещение функций в Linux
- Проблемы C-подобных языков. Где находиться типу: справа или слева? (Теория)
- Откуда в языке Си появился синтаксис указателей, и для чего он предназначался изначально
- Как в языке Си вызвать функцию, для которой известен адрес вызова в виде числа
- Вызов функции по известному адресу в языке Си — абстрактный тип данных указателя на функцию
- Структуры в языке Си. Определения структур в сравнении с языком C++
- Структуры в языке Си. Указатели на структуры
- Структуры в языке Си. Массивы структур
- Операция запятая «,» в языке Си и Си++
- Самый быстрый и оптимальный способ копирования строк в Cи и C++