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

Рейтинг: 0Ответов: 1Опубликовано: 07.03.2023

Перепробовал все что только можно. Но пока знаний нет, то все сводится к бездумному изменению буковок, потому прошу пояснить как оно дожно работать. У меня есть программа, в мейн оглашаются указатель на массив и переменная отвечающая за его длину. В функции 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;
}

Ответы

▲ 0

Вы должны передавать указатель на указатель 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);