Как передать динамический массив в функцию Си?
Перепробовал все что только можно. Но пока знаний нет, то все сводится к бездумному изменению буковок, потому прошу пояснить как оно дожно работать. У меня есть программа, в мейн оглашаются указатель на массив и переменная отвечающая за его длину. В функции scan создается массив, также выводится, но вообще дожлен выводиться через функцию print, но она не работает, как и остальные функции. Насколько я понимаю, такая передача назвается по указателю. Читал про это, но все тщетно. Прошу изменить мою програму чтоб работала правильно и обяснить как она работает.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void scan(int** p, int N);
void print(int* p, int N);
void add(int** p, int N);
void del(int* p, int N);
void freedom(int* p);
int main(){
int* p = NULL;
int N = 0;
system("chcp 1251"), system("cls");
while(1){
printf("+====================================+\n");
printf("|1 — создать масив |\n");
printf("|------------------------------------|\n");
printf("|2 — напечатать масив |\n");
printf("|------------------------------------|\n");
printf("|3 — добавить один/несколько элементов |\n");
printf("|------------------------------------|\n");
printf("|4 — удалить один/несколько элементов|\n");
printf("|------------------------------------|\n");
printf("|5 — освободить память |\n");
printf("|------------------------------------|\n");
printf("|0 — выход |\n");
printf("+====================================+\n");
char menubar;
scanf("%d", &menubar);
switch(menubar){
case 0:
printf("\nВыход");
return 0;
case 1:
scan(&p, N);
system("pause");
system("cls");
break;
case 2:
print(p, N);
system("pause");
system("cls");
break;
case 3:
add(&p, N);
system("pause");
system("cls");
break;
case 4:
del(p, N);
system("pause");
system("cls");
break;
case 5:
freedom(p);
system("pause");
system("cls");
break;
default:
printf("\nОшибка");
break;
}
continue;
}
}
void scan(int** p, int N){
int *tmp;
printf("Введите размер массива: ");
scanf("%d", &N);
tmp = (int*) malloc(N*sizeof(int));
if(NULL==tmp){
printf("Недостаточно памяти\n");
exit(1);
}
int a;
srand(time(NULL));
printf("Для автозаполнения нажмите \"1\", для заполнения вручную нажмите \"0\": ");
scanf("%d", &a);
if(a==1)
for (int i=0; i<N; ++i)
*(tmp+i) = rand()%10;
if(a==0){
printf("Введите элементы через пробел: ");
for (int i=0; i<N; ++i)
scanf("%d", &tmp[i]);
}
for (int i=0; i<N; ++i){
printf("%3d", tmp[i]);
}
printf("\n");
*p = tmp;
}
void print(int* p, int N){
for (int i=0; i<N; i++)
printf("%3d", p[i]);
printf("\n");
}
void add(int** p, int N){
int* tmp;
int M, i;
printf("Введите количество элементов, которые нужно добавить в массив: ");
scanf("%d", &M);
tmp = (int*) realloc(tmp, (N+M)*sizeof(int));
if(NULL==tmp){
printf("Недостаточно памяти\n");
exit(1);
}
printf("Введите на какое место поставить первый элемент (от 1 до N включительно): ");
int x;
scanf("%d", &x);
printf("Введите значения этих элементов через пробел: ");
for (i=N+M-1; i>=x+M-1; --i) {
tmp[i] = tmp[i-M];
}
for (int i=x-1; i<x+M-1; ++i) {
scanf("%d", &tmp[i]);
}
N += M;
*p = tmp;
printf("\n");
}
void del(int* p, int N){
int M;
printf("Введите количество элементов, которые нужно удалить: ");
scanf("%d", &M);
if (M <= 0) {
printf("Число елементов для удаления должно быть больше нуля!\n");
return;
}
if (M > N) {
printf("Число елементов для удаления не должно быть больше чем размер массива!\n");
return;
}
printf("С какого елемента начинать удаление (від 1 до N включительно): ");
int x;
scanf("%d", &x);
if (x < 1 || x > N) {
printf("Можно начинать удаление только від 1 до N включительно!\n");
return;
}
for (int i = x - 1; i < N - M; ++i) {
p[i] = p[i+M];
}
p = (int*) realloc(p, (N-M)*sizeof(int));
if (NULL == p){
printf("Недостаточно памяти\n");
exit(1);
}
N -= M;
}
void freedom(int* p){
if (NULL == p){
printf("Недостаточно памяти\n");
exit(1);
}
free(p);
p = NULL;
}
Ответы (1 шт):
Вы должны передавать указатель на указатель int** p (адрес указателя) в функции, которые выделяют или перевыделяют память. Для того, чтобы новое значение вернулось (было видимо) в main(). В остальные функции можно передавать указатель по значению, т.к. они не изменяют сам массив - изменение данных в массиве это другое.
Также, вам нужно передавать адрес переменной N - размер массива в те функции, которые изменяют этот самый размер. А то сейчас получается, что в функциях scan() add() del() вы меняете размер, но в main() этого не видно.
Вот так будет работать
void print(int* p, int N)
{
for (int i=0; i<N; i++)
printf("%3d", p[i]);
printf("\n");
}
void scan(int** p, int* N)
{
int *tmp;
scanf("%d", N);
tmp = (int*) malloc(*N * sizeof(int));
*p = tmp;
}
void add(int** p, int* N)
{
int* tmp;
int M, i;
scanf("%d", &M);
tmp = (int*) realloc( tmp, (*N+M) * sizeof(int) );
*N += M;
*p = tmp;
}
int main()
{
int* p = NULL;
int N = 0;
scan( &p, &N);
print( p, N);
}
Чтобы не путаться, сделайте отдельные функции для увеличения и уменьшения выделенной памяти под массив и вызывайте их из своих функций. Например:
incArray(int **p, int oldsize, int newsize);
decArray(int **p, int oldsize, int newsize);
Так вы разделите логику программы от логики работы с памятью.
Также, сейчас у вас очень неоптимальна работа с памятью. Вызвав 5 раз функцию для добавления 1 элемента, вы 5 раз перевыделите память, а это дорогая операция с точки зрения производительности. Гораздо быстрее если у вас будет 2 размера - размер буффера под массив (сколько памяти выделено) и текущий размер массива (количество элементов в нем). Тогда при добавлении/удалении элемента вы не перевыделяете память, а только дописываете элемент на свободное место. Перевыделение памяти делаете только тогда, когда его не хватает. Такая логика работы у динамических массивов std::vector<>.
Ещё одна ошибка - неопределенное поведение программы при затирании соседней памяти вот в этом фрагменте функции main(). У вас menubar занимает 1 байт, а в scanf("%d", вы передаете адрес по которому записывается 4 байта, т.к. %d - идентификатор для int. На моём компиляторе затирается переменная N, в которой должен храниться размер массива. А вообще может затереться всё что угодно.
char menubar;
scanf("%d", &menubar); // неправильно!!! - передаем адрес переменной размером 1 байт для записи 4 байт
// должно быть
char menubar;
scanf("%hhd", &menubar);
// или
int menubar;
scanf("%d", &menubar);