1

Тема: Access violation у динамічній пам'яті

Привіт. Виконую завдання: Дано інформацію про готель (скільки чоловік і в якому номері
проживають), де i – номер корпусу, j – номер поверху, k – номер кімнати.
Визначити різницю між кількістю зайнятих та порожніх номерів у n-му
корпусі на z-му поверсі використовуючи динамічне виділення пам’яті. Для звертання до елементів
масивів застосувати адресну арифметику.
На вигляд ніби все гаразд, але при компіляції похибка (Unhandled exception at 0x00007FF68B812845 in labb5.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.).

#include <iostream>
using namespace std;

int main() {
    int i = 0, j = 0, k = 0;

    do {
        cout << "\nHow much body of buildings?: ";
        cin >> i;
    } while (i < 0);
    do {
        cout << "\nHow much floors?: ";
        cin >> j;
    } while (j < 0);
    do {
        cout << "\nHow much rooms?: ";
        cin >> k;
    } while (k < 0);


    int*** Hotel = new int** [i];
    for (int b = 0; b < i; b++) {
        Hotel[i] = new int* [j];
        for (int f = 0; f < j; f++) {
            Hotel[i][j] = new int[k];
        }
    }

    for (int b = 0; b < i; b++) {
        cout << "\nBody of the building " << b + 1 << " : ";
        for (int f = 0; f < j; f++) {
            cout << "\nFloor " << f + 1 << " : ";
            for (int r = 0; r < k; r++) {
                do {
                    cout << "\nRoom " << r + 1 << " : ";
                    cin >> *(*(*(Hotel + b) + f) + r);
                } while (6 < (*(*(*(Hotel + b) + f) + r)) < 0);
            }
        }
    }

    short int n, z, e = 0, f = 0, d;
    cout << "\nDifference between: \n";

    do {
        cout << "\nBody of the building: ";
        cin >> n;
    } while (n > i);

    do {
        cout << "\nFloor: ";
        cin >> z;
    } while (z > j);


    for (int r = 0; r < k; r++) {
        if (*(*(*(Hotel+n)+z)+r) >= 1) {
            f++;
        }
        if (*(*(*(Hotel+n)+z)+r) == 0) {
            e++;
        }
    }

    d = f - e;

    cout << "\nDifference between filled and empty rooms is: " << d << endl;

    cout << "Full: " << f << endl;
    cout << "Empty: " << e << endl;

    for (int b = 0; b < i; b++)
    {
        for (int f = 0; f < j; f++)
        {
            for (int r = 0; r < k; r++)
            {
                delete[] Hotel[k];
            }
            delete[] Hotel[i][j];
        }
        delete[] Hotel[i];
    }
    delete[] Hotel;

    system("pause");
    return 0;
}

2

Re: Access violation у динамічній пам'яті

Cyber Tortik написав:

Привіт. Виконую завдання: Дано інформацію про готель (скільки чоловік і в якому номері
проживають), де i – номер корпусу, j – номер поверху, k – номер кімнати.
Визначити різницю між кількістю зайнятих та порожніх номерів у n-му
корпусі на z-му поверсі використовуючи динамічне виділення пам’яті. Для звертання до елементів
масивів застосувати адресну арифметику.
На вигляд ніби все гаразд, але при компіляції похибка (Unhandled exception at 0x00007FF68B812845 in labb5.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.).

Можливо це щось банальне і просте, але тлише недавно почав вичати програмування і цього можу не знати. На перед дякую!

#include <iostream>
using namespace std;

int main() {
    int i = 0, j = 0, k = 0;

    do {
        cout << "\nHow much body of buildings?: ";
        cin >> i;
    } while (i < 0);
    do {
        cout << "\nHow much floors?: ";
        cin >> j;
    } while (j < 0);
    do {
        cout << "\nHow much rooms?: ";
        cin >> k;
    } while (k < 0);


    int*** Hotel = new int** [i];
    for (int b = 0; b < i; b++) {
        Hotel[i] = new int* [j];
        for (int f = 0; f < j; f++) {
            Hotel[i][j] = new int[k];
        }
    }

    for (int b = 0; b < i; b++) {
        cout << "\nBody of the building " << b + 1 << " : ";
        for (int f = 0; f < j; f++) {
            cout << "\nFloor " << f + 1 << " : ";
            for (int r = 0; r < k; r++) {
                do {
                    cout << "\nRoom " << r + 1 << " : ";
                    cin >> *(*(*(Hotel + b) + f) + r); ---------------Похибку показує тут.
                } while (6 < (*(*(*(Hotel + b) + f) + r)) < 0);
            }
        }
    }

    short int n, z, e = 0, f = 0, d;
    cout << "\nDifference between: \n";

    do {
        cout << "\nBody of the building: ";
        cin >> n;
    } while (n > i);

    do {
        cout << "\nFloor: ";
        cin >> z;
    } while (z > j);


    for (int r = 0; r < k; r++) {
        if (*(*(*(Hotel+n)+z)+r) >= 1) {
            f++;
        }
        if (*(*(*(Hotel+n)+z)+r) == 0) {
            e++;
        }
    }

    d = f - e;

    cout << "\nDifference between filled and empty rooms is: " << d << endl;

    cout << "Full: " << f << endl;
    cout << "Empty: " << e << endl;

    for (int b = 0; b < i; b++)
    {
        for (int f = 0; f < j; f++)
        {
            for (int r = 0; r < k; r++)
            {
                delete[] Hotel[k];
            }
            delete[] Hotel[i][j];
        }
        delete[] Hotel[i];
    }
    delete[] Hotel;

    system("pause");
    return 0;
}

3

Re: Access violation у динамічній пам'яті

Від модератора: будь ласка, не пишіть у чужі теми, у вас своя задача. Зокрема тому, що це порушує Правила.

Похибка - це відхилення значення від правильного, а у вас тут усе ж таки помилка.
Загальне:
- не "економте час" на читаності коду, ви потім значно більше часу втратите, намагаючися знайти помилку.
- давайте змінним зрозумілі назви. i,j,k,b,f,r - це що таке? Як тут не заплутатися? В умові чітко сказано: є готель, у нього корпуси, поверхи та кімнати. Якщо розмірності будуть позначені як building_num, floor_num і room_num, а індекси, скажімо, building_idx, floor_idx і room_idx - то правда ж буде легше?
- замість вказівникової арифметики використовуйте для масивів індексний запис - замість *(a+i) пишіть a[i]. Тоді буде не
*(*(*(Hotel + b) + f) + r), а Hotel[b][f][r], що значно зрозуміліше, правда? А якщо змінні назвати нормально, то Hotel[building_idx][floor_idx][room_idx]. Взагалі чудово. Ну, або хоча б i,j,k замініть на щось, що натягає на їхнє значення.
- Це не Python, вираз 6 < (*(*(*(Hotel + b) + f) + r)) < 0 означає зовсім не те, що б вам хотілося. Спершу буде обчислене значення 6<(*(*(*(Hotel + b) + f) + r)) - true або false, далі перетворене з bool на int (тобто на 1 чи 0) і порівняно з 0, тому весь вираз - завжди false. Логічні умови треба об'єднувати операторами І (&&) та АБО (||).
- не треба перевіряти одну й ту саму умову двічі на протилежні значення, є інструкція else якраз для таких випадків.
- стежте за кількістю new/delete. Їх має бути однакова кількість. У вас 4 delete і лише 3 new. Щось не так.

Ваша помилка виникає точно не при компіляції (це помилка часу виконання), і схоже, що не в цьому рядку. Можливо, i, j чи k дорівнюють 0. Можливо, при некоректному звільненні пам'яті. Можливо, неправильно задані n і z (мають бути від 0 до i-1 та j-1 відповідно, а у вас i та j дозволені).

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

4

Re: Access violation у динамічній пам'яті

Ти намагаєшся дістатись до ділянки пам'яті, до котрої доступ тобі xC0000005: Access violation reading location. Причини описані вище, у попередньому повідомлені. Я розумію, що ти починаєш вчити С++. Але у твоїй програмі забагато зайвих рухів. У завдані сказано: "Визначити різницю між кількістю зайнятих та порожніх номерів у N-му корпусі на Z-му поверсі". Банально знайти кількість кімнат на поверсі котрі більші за НУЛЬ, та знайти різницю.
Думаю викладач, дав це завдання, щоб виробити навички роботи ДИНАМІЧНИМ РОЗПОДІЛОМ ПАМ'ЯТІ. Як представляються дані у пам'яті КУПІ, в даному випадку та доступом до цих комірок за допомогою вказівників. І акцент на виконанні завдання роби саме на це!
Твою структуру можна представити, як БАГАТОМІРНИЙ МАСИВ (твій випадок із купою new та delete).

Прихований текст
// Багатовимірний масив
#include <stddef.h>

#define HEIGHT 3
#define WIDTH 5

int main()
{
    int t[HEIGHT][WIDTH];

    size_t h, w;
    for (h = 0; h < HEIGHT; h++)
    {
        for (w = 0; w < WIDTH; w++)
            t[h][w] = (h + 1) * (w + 1);
    }

    return 0;
}

чи ПСЕВДО-БАГАТОМІРНИЙ МАСИВ

Прихований текст
// Псевдо-багатовимірний масив
#include <stddef.h>

#define HEIGHT 3
#define WIDTH 5

int main()
{
    int t[HEIGHT * WIDTH];

    size_t h, w;
    for (h = 0; h < HEIGHT; h++)
    {
        for (w = 0; w < WIDTH; w++)
            t[h * WIDTH + w] = (h + 1) * (w + 1);
    }

    return 0;
}

Ось шаблон першого варіанту для твого завдання:

#include <cstddef>
#include <new>

using namespace std;

int main()
{
    const size_t BUILD = 2; // К-ть корпусів
    const size_t FLOOR = 3; // К-ть поверхів
    const size_t ROOM = 4;    // к-ть кімнат

    int ***hotel = nullptr;

    // Створюємо HOTEL - ТРИМІРНИЙ МАСИВ.
    hotel = new int **[BUILD]; // Виділяємо пам'ять під корпусу ...
    for (size_t b = 0u; b < BUILD; ++b)
    {
        *(hotel + b) = new int *[FLOOR]; // ... тут під поверхи ...
        for (size_t f = 0u; f < FLOOR; ++f)
            *(*(hotel + b) + f) = new int[ROOM]; // ... ну і під кімнати
    }

    // У 2-й кімнати на 2-му поверсі 1-го корпусу ...
    size_t bid = 0,
           fid = 1,
           rid = 1;

    *(*(*(hotel + bid) + fid) + rid) = 7; // ... проживає 7-ро людей.

    // Тут знищуємо масив HOTEL
    for (size_t b = 0; b < BUILD; ++b)
    {
        for (size_t f = 0; f < FLOOR; ++f)
            delete[] * (*(hotel + b) + f); // Знищуємо поверх з кімнатами
        delete[] * (hotel + b);               // Ось тут знищуємо вказівник, котрий вказує на масив поверхів
    }
    delete[] hotel; // А тут - вказівник на масив корпусів

    return 0;
}

Ще один шаблон для запису тільки псевдо-тримірного масиву:

#include <cstddef>
#include <new>

using namespace std;

int main()
{
    const size_t BUILD = 2; // Корпуси
    const size_t FLOOR = 3; // поверхи
    const size_t ROOM = 4;    // кімнати

    int *hotel = nullptr;
    size_t SIZE = BUILD * FLOOR * ROOM;

    // Тут виділяємо ПСЕВДО-ТРИМІРНИЙ МАСИВ
    hotel = new int[SIZE];

    // у 4-й кімнаті на 2-му поверсі 1-го корпусу ...
    size_t bid = 0,
           fid = 1,
           rid = 3;

    *(hotel + (bid * FLOOR * ROOM) + (ROOM * fid) + rid) = 3;  // ... проживає 3 особи ))

    // ось спосіб доступу до елементів масиву тільки з []
    bid = 1;
    fid = 1;
    rid = 0;

    hotel[bid * FLOOR * ROOM + ROOM * fid + rid] = 6;

    delete[] hotel; // Тут знищуємо

    return 0;
}

Як бачиш різниця у чистоті коду помітна. І легше працювати із другим варіантом. Потрібно лише правильно організувати доступ до елементів масиву. Ось тобі і динамічне виділення пам'яті та адресна арифметика із вказівниками.
Отже правильно розподіляй та знищуй пам'ять, вірно роби межі для вкладених циклів і слідкуй за індексами, щоб не виходити за межі. Успіхів у навчанні.

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