Colinp
@Colinp
Учу C++

Возможно ли избежать ошибку чтения в массиве если алгоритм задействует ячейку которой нет?

Привет! Суть ошибки такая. Я делаю симуляцию клеточного аппарата "Жизнь". При исполнении алгоритма, который меняет состояние клетки, он проверяет ячейку которой нет (не выделена память). Что в таком случае стоит сделать? Стоит ли выделить массив больше дабы избежать ошибки?

Некоторое пояснение:
Алгоритм проходит по двумерному массиву, к примеру 5x5. И при исполнении, когда он находится на ячейке к примеру [0][0], то он проверяет ячейки [-1][0] и т.п. Т.е пытается получить доступ фактически ни к чему.

Что в этом случае будет сделать легче или разумнее? Изучаю C++ некоторое время и поэтому вместо копирования с чего-либо решил спросить у комьюнити хабра. Заранее спасибо, с радостью приму любую критику!

Код тут
#define EMPTY_CELL ' '
#define CLAIM_CELL '#'

#include <cstdio>
#include <windows.h> //смешная библиотека, ничего не понимаю


const int cols = 5, rows = 5;
const bool isInfinit = true;
int steps = 25;
const int delayTime = 1000;

void Log(const char* msg) {
    printf(msg);
    printf("\n");
}// а
void LogPrefers() {
    printf("PREFERS: \n");
    printf("\t cols %d\n", cols);
    printf("\t rows %d\n", rows);
    printf("\t infinity %d\n", isInfinit);
    printf("\t steps %d\n", steps);
    printf("\t delay %d\n", delayTime);
}

int main(int argc, char* argv[]) {
    // Initialize simulation field
    LogPrefers();
    Log("Start initializing <?");
    char** field = new char* [cols];
    for (size_t i = 0; i < cols; ++i) {
        field[i] = new char[rows];
    }
    Log("Memory initialized <!");
    Log("Initializing by fill <?");
    for (size_t i = 0; i < cols; ++i) {
        for (size_t k = 0; k < rows; ++k) {
            field[i][k] = ' ';
        }
    }
    field[2][2] = '#';
    Log("Memory initialized <!");
    Log("Start simulation <?");
    Log("Started <!");
    // Simulation
    do {
        int temp = 0;
        for (size_t i = 0; i < cols; ++i) {
            for (size_t k = 0; k < rows; ++k) { // Господи, заработай пожалуйста с первого раза, аминь
                // Проверка соседей вокруг клетки.
                if (field[i - 1][k - 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i][k - 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i + 1][k - 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i - 1][k] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i + 1][k] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i + 1][k + 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i][k + 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                else if (field[i - 1][k + 1] != NULL && field[i - 1][k - 1] == CLAIM_CELL) temp += 1;
                if (temp == 3 || temp == 2) {
                    field[i][k] == '#';
                }
                else {
                    field[i][k] == ' ';
                }
                temp = 0; // Сбрасываем счётчик!111
            }
        }
        for (size_t i = 0; i < cols; ++i) {
            printf("\n");
            for (size_t k = 0; k < rows; ++k) {
                printf("%c", field[i][k]);
            }
        }
        steps -= 1;
        //Sleep(delayTime);
    } while (steps > 0 || isInfinit);

    // Free ramm from simulation field  linux suck
    Log("! END !");
    for (int i = 0; i < cols; ++i) {
        delete[] field[i];
    }
    delete[] field;

    return 0;
}
  • Вопрос задан
  • 104 просмотра
Решения вопроса 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Для игры "жизнь" есть несколько вариантов:
1) Увеличить поле на 2 клетки по каждому измерению, поле будет храниться с 1, а индексы 0 и n+1 - всегда будут пустыми. Потребление памяти это почти не увеличит, а код упростит.
2) Если соседние клетки считаются циклами, то можно границы области 3x3 пересечь с полем:
for (int nx = max(0, x-1); nx < min(x+2, n); ++nx) {
  for (int ny = max(0, y-1); ny < min(y+2, n); ++ny) {
    if (nx == x && ny == y) continue;
    // {nx, ny} - сосед в поле, обрабатываем его.
  }
}

Можно код чуть ускорить, предподсчитав границы.
3) Более читаемый, но чуть более медленный метод - явно проверять, а не за границей ли соседняя клетка:
for (int nx = x-1; nx <= x+1; ++nx) {
  for (int ny = y-1; ny <= y+1; ++ny) {
    if ((nx == x && ny == y) || nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
    // {nx, ny} - соседняя клетка.
  }
}


Я бы просто раздул поле - так код сильно проще.

Но вотрой метод так легко реализуется только тут, где нужно именно количетсво живых соседей и можно просто игнорировать клетки вне поля.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
AshBlade
@AshBlade
Просто хочу быть счастливым
Граничные значения обрабатывать особенным образом:
- Если это первая и последняя строка, то не берем в учет верхнюю или нижнюю строки соответственно
- Если это самый левый или правый столбец, то не берем в учет левый или правый (относительно текущего) столбец соответственно

Т.е. в каждый цикл дополнительно вставляются окаймляющие участки (для этих случаев)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы