1 Востаннє редагувалося leofun01 (17.10.2023 19:19:02)

Тема: Не розумію у чому помилка

Добрий день! Я зараз пишу код для сортування злиттям (Боуза-Нельсона та Неймановське), виникла така проблема: мені потрібно зробити аналіз роботи алгоритмів при різних типів даних. Вирішила використовувати char, int та float, але при виведенні масиву (неважливо якого типу) нічого не виводиться, тільки порожнеча. Підкажіть будь ласка, де саме у мене помилка? Перевіряла все - заповнення, створення - виділення пам'яті, начебто все ок.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Глобальні змінні для коректної роботи
int arr_size, arr_c, arr_f;
int *array;
char *arr;
float *arr_flo;
int data_type;

void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;

    int Ar[n1], Br[n2]; // тимчасові підмасиви

    for (i = 0; i < n1; i++)
        Ar[i] = arr[l + i];
    for (j = 0; j < n2; j++)
        Br[j] = arr[m + 1 + j];

    i = 0;
    j = 0;
    k = l;
    while (i < n1 && j < n2) {
        if (Ar[i] <= Br[j]) {
            arr[k] = Ar[i];
            i++;
        }
        else {
            arr[k] = Br[j];
            j++;
            k++;
        }
    }
    while (i < n1) {
        arr[k] = Ar[i];
        i++;
        k++;
    }
    while (j < n2) {
        arr[k] = Br[j];
        j++;
        k++;
    }
}
// функція сортування злиттям
void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;

        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);

        merge(arr, l, m, r); // злиття всіх половин
    }
}
void boseNelsonSort(int arr[], int n) {
    for (int groupSize = 1; groupSize < n; groupSize *= 2) {
        for (int left = 0; left < n-1; left += 2*groupSize) {
            int mid = left + groupSize - 1;
            int right = (left + 2*groupSize - 1 < n-1) ? (left + 2*groupSize - 1) : (n-1);
            merge(arr, left, mid, right);
        }
    }
}
// Вивід масивів 3 типів
void printArray(int A[], int size) {
    int i;
    for (i = 0; i < size; i++) {
        printf("%d ", A[i]);
    }
    printf("\n");
}
void printChar(char A[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%c ", A[i]);
    }
    printf("\n");
}
void printFloat(float A[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%.2f ", A[i]);
    }
    printf("\n");
}
// це створення int масиву
int *create_int(int *arr_size) {
    system("cls");
    printf("\n---------------------");
    printf("\nВведіть розмір масиву: ");
    scanf("%d", arr_size);

    int *arr = (int *)malloc((*arr_size) * sizeof(int));

    srand(time(NULL));
    for (int i = 0; i < *arr_size; i++) {
        arr[i] = rand() % 500;
    }
    return arr;
}
// створення char масиву
char *create_char(int *arr_c) {
    system("cls");
    printf("\n---------------------");
    printf("\nВведіть розмір масиву: ");
    scanf("%d", arr_c);

    char *arrchar = (char *)malloc((*arr_c) * sizeof(char));

    srand(time(NULL));
    for(int i = 0; i < *arr_c; i++) {
        arrchar[i] = rand() % 26;
    }
    return arrchar;
}
// для створення float масиву
float *create_float(int *arr_f) {
    system("cls");
    printf("\n---------------------");
    printf("\nВведіть розмір масиву: ");
    scanf("%d", arr_f);

    float *arr_flo = (float *)malloc((*arr_f) * sizeof(float));

    srand(time(NULL));
    for(int i = 0; i < *arr_f; i++) {
        arr_flo[i] = 0.1 * (rand() % 1001);
    }
    return arr_flo;
}
void menu() {
    int ch_1;
    printf("\n------------------");
    printf("\n-------МЕНЮ-------");
    printf("\nЯкий тип даних використовувати");
    printf("\n|1| - Int\n|2| - Char\n|3| - Float\n");
    printf("\nВаш вибір -->  ");
    scanf("%d", &ch_1);

    data_type = ch_1;

    switch(ch_1) {
        case 1:
            if (array != NULL) {
                free(array);
            }
            array = create_int(&arr_size);
            break;
        case 2:
            if (arr != NULL) {
                free(arr);
            }
            arr = create_char(&arr_c);
            break;
        case 3:
            if (arr_flo != NULL) {
                free(arr_flo);
            }
            arr_flo = create_float(&arr_f);
            break;
        default:
            printf("\nОберіть тип даних.");
            break;
    }
}

int main() {
    system("chcp 65001");
    int arr_size, arr_c, arr_f;
    int choice;
    // Для уникнення помилок та для ініціалізації покажчиків
    int *array = NULL;
    char *arr = NULL;
    float *arr_flo = NULL;
    menu();
    while (1) {
        printf("\n|1| - Сортування злиттям Неймана\n|2| - Сортування злиттям Боуза-Нельсона\n|3| - Перегляд масиву\n|4| - Назад\n|5| - Вихід\n");
        printf("\nВаш вибір --->  ");
        scanf("%d", &choice);
        system("cls");
        switch (choice) {
            case 1:
                mergeSort(array, 0, arr_size - 1);
                printf("\n---------------------");
                printf("\nВідсортований масив:\n\n");
                printArray(array, arr_size);
                break;
            case 2:
                boseNelsonSort(array, arr_size);
                printf("\n---------------------");
                printf("\nВідсортований масив:\n\n");
                printArray(array, arr_size);
                break;
            case 3:
                if (data_type == 1) {
                    printf("\nПочатковий масив:\n\n");
                    printArray(array, arr_size);
                }
                else if (data_type == 2) {
                    printf("\nПочатковий масив:\n\n");
                    printChar(arr, arr_c);
                }
                else if (data_type == 3) {
                    printf("\nПочатковий масив:\n\n");
                    printFloat(arr_flo, arr_f);
                }
                break;
            case 4:
                if(array != NULL) {
                    free(array);
                }
                else if(arr != NULL) {
                    free(arr);
                }
                else if(arr_flo != NULL) {
                    free(arr_flo);
                }
                menu();
                break;
            case 5:
                exit(0);
                break;
            default:
                printf("\nВиберіть дію із запропонованих..\n\n");
                break;
        }
    }
    free(array);
    return 0;
}

2

Re: Не розумію у чому помилка

MaxPluto написав:
// ГЛОБАЛЬНІ ЗМІННІ ДЛЯ КОРЕКТНОЇ РОБОТИ

Цей коментар викликає у мене змішані відчуття. Без глобальних змінних добитись коректної роботи вашої програми - марна справа?

MaxPluto написав:
int *create_int(int *arr_size)

Чому arr_size вказівник, це ж лише розмір, так?

І так, а де власне ви задаєте цей розмір?

<source>:234:26: warning: 'arr_size' may be used uninitialized [-Wmaybe-uninitialized]
  234 |                 mergeSort(array, 0, arr_size - 1);
      |                 ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
<source>:217:9: note: 'arr_size' was declared here
  217 |     int arr_size, arr_c, arr_f;
      |         ^~~~~~~~
<source>:254:30: warning: 'arr_c' may be used uninitialized [-Wmaybe-uninitialized]
  254 |                     printChar(arr, arr_c);
      |                     ~~~~~~~~~^~~~~~~~~~~~
<source>:217:19: note: 'arr_c' was declared here
  217 |     int arr_size, arr_c, arr_f;
      |                   ^~~~~
<source>:259:31: warning: 'arr_f' may be used uninitialized [-Wmaybe-uninitialized]
  259 |                     printFloat(arr_flo, arr_f);
      |                     ~~~~~~~~~~^~~~~~~~~~~~~~~~
<source>:217:26: note: 'arr_f' was declared here
  217 |     int arr_size, arr_c, arr_f;
      |                          ^~~~~

Гадаю ваша "проблема" буде вирішена, коли ви виправите всі ці попередження.

Подякували: koala, leofun012

3 Востаннє редагувалося koala (17.10.2023 14:18:14)

Re: Не розумію у чому помилка

Підтримую wander, проблема саме в тому, що ви створили декілька локальних змінних, потім не зуміли передати через них дані з функції і тому створили глобальні змінні з такими ж назвами, але локальні в main не видалили, і виходить, що заповнюються глобальні змінні, а в функцію передаються локальні. Якщо вже послуговуєтеся глобальними змінними - видаліть локальні. Хоча в цілому глобальні змінні - це погано.

wander написав:

І так, а де власне ви задаєте цей розмір?

У функції create_int, дивіться уважніше.

Подякували: wander1

4

Re: Не розумію у чому помилка

koala написав:
wander написав:

І так, а де власне ви задаєте цей розмір?

У функції create_int, дивіться уважніше.

І справді, видно мій мозок просто відмовився сприйняти задавання розміру всередині такої функції *SCRATCH*

5

Re: Не розумію у чому помилка

Ну і просто підозріле місце:

        if (Ar[i] <= Br[j]) 
        { 
            arr[k] = Ar[i];
            i++; 
        } 
        else 
        { 
            arr[k] = Br[j];
            j++;
        k++; 
        }

відступ у k++ натякає, що ви хотіли поставити k++ поза if-else, але зараз воно стоїть лише в else.

Подякували: FakiNyan, leofun012

6

Re: Не розумію у чому помилка

Дякую за зауваження!

Глобальні змінні вирішила використовувати, коли у мене не вийшло з локальними, тобто почалася плутанина із передаванням у функції, бо для повторного створення масивів я створила функцію menu, щоб просто викликати її та все розкласти по полицям (хоча у мене це так і не вийшло :') ).

Я не зовсім розумію момент, коли зроблю розміри масивів та самі масиві локальними, доводиться передевати у main тоді розміри, це буде нормальним?

7

Re: Не розумію у чому помилка

MaxPluto написав:

доводиться передевати у main тоді розміри, це буде нормальним?

Так, в C це норма - передавати масив (тобто покажчик на перший елемент) і розмір. Альтернатива - покажчики на початок і кінець, ну або ж використовується якесь особливе значення в самому масиві для визначення кінця, як нульовий символ у рядках. Власне, це відбувається в практично усіх мовах (бо іншого шляху просто немає), просто не треба явно це вказувати.

8

Re: Не розумію у чому помилка

Просто для ілюстрації: беремо GSL, досить поширену бібліотеку для наукових обчислень, і бачимо:

double gsl_stats_mean(const double data[], size_t stride, size_t n)

Тут передається не лише розмір, а ще й крок для роботи з багатовимірними масивами.

Подякували: leofun011

9

Re: Не розумію у чому помилка

А, ще: якщо я правильно зрозумів цей фрагмент, то ідіоматично на C/C++ це записується як

        if (Ar[i] <= Br[j]) 
            arr[k++] = Ar[i++];
        else 
            arr[k++] = Br[j++];

чи навіть

        arr[k++] = Ar[i] <= Br[j] ? Ar[i++] : Br[j++];

А ще там не обов'язково переносити один з масивів у тимчасовий, але я у ваших літерах трохи заплутався.

Подякували: leofun011

10

Re: Не розумію у чому помилка

Ще одна порада, трохи глибша.

Масив - це неперервна область пам'яті, десь така:

| a[0] | a[1] | a[2] | ... | a[n-1] |

Адреса всього масиву дорівнює адресі нульової комірки, а усі інші адреси в масиві обчислюються додаванням:

&a[2] == &(a+2)

Причому ім'я масиву позначає саме цю адресу:

a==&a[0]

Ну так от, з цього випливає, що якщо вам треба у масиві a довжиною len_a передати у функцію комірки від l до r, то ви можете замість a, l і r передавати &a[l] і r-l - точки зору функції, в неї передається масив і довжина.

void mergeSort(int arr[], unsigned n) {
    if(n>1) {
        int m = n / 2;

        mergeSort(&arr[0], m); //можна було написати (arr, m), але я записав так для наочності
        mergeSort(&arr[m], n - m);

        merge(arr, m, n);
    }
}

Це виглядатиме десь так:

| a[0] | a[1] | a[2] | ... | a[m-1]| | a[m] | ... |a[n-1] | << так це бачить функція
| a[0] | a[1] | a[2] | ... | a[n-1]| | << так це бачить функція в першому виклику
                                     | a[0] | ... | a[n-1]| <<а так - в другому