Двумерные массивы

Двумерный массив — это таблица

Если одномерный массив — полка с ячейками, то двумерный — шкаф: несколько полок, на каждой одинаковое число ячеек. Элемент задаётся двумя индексами: a[строка][столбец].

#define MAX_SIZE 20

int a[MAX_SIZE][MAX_SIZE];      /* максимум 20 × 20 */
int b[3][4];                    /* 3 строки, 4 столбца — 12 элементов */
        столбец 0  столбец 1  столбец 2
строка 0    1          2          3
строка 1    4          5          6

Важно. Первый индекс — строка, второй — столбец. Путаница с порядком индексов — ошибка №1 при работе с матрицами.

Заполнить матрицу можно и при объявлении:

int m[2][3] = {
    { 1, 2, 3 },
    { 4, 5, 6 },
};

В лабораторной работе №2 многомерный массив хранится в статическом массиве int a[MAX_SIZE][MAX_SIZE]; динамическое выделение памяти (malloc) не используется. Размеры вводит пользователь: 1 ≤ N, M ≤ MAX_SIZE.

Чтение матрицы с клавиатуры

Сначала N и M (строки и столбцы), затем N × M чисел. Размеры обязательно проверяем:

int n, m;
int a[MAX_SIZE][MAX_SIZE];

printf("Введите N и M: ");
if (scanf("%d %d", &n, &m) != 2)
{
    printf("Ошибка: размеры должны быть целым числом\n");
    return 1;
}

if (n < 1 || m < 1 || n > MAX_SIZE || m > MAX_SIZE)
{
    printf("Ошибка: размеры должны быть от 1 до %d\n", MAX_SIZE);
    return 1;
}

for (int i = 0; i < n; i++)          /* строки */
{
    for (int j = 0; j < m; j++)      /* столбцы */
    {
        if (scanf("%d", &a[i][j]) != 1)
        {
            printf("Ошибка: ожидалось целое число\n");
            return 1;
        }
    }
}

Строки и столбцы можно вводить как в одну строку, так и построчно — scanf пропускает пробелы и переводы строк.

Вывод таблицей

Матрицу печатают вложенными циклами, а для выравнивания используют ширину поля %4d:

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        printf("%4d", a[i][j]);
    }
    printf("\n");        /* перевод строки после каждой строки матрицы */
}

Обход: вложенные циклы

Внешний цикл идёт по строкам, внутренний — по столбцам. Для матрицы нужны два вложенных цикла:

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        /* работаем с a[i][j] */
    }
}

Типовые задачи

Сумма всех элементов

int sum = 0;
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        sum += a[i][j];
    }
}
printf("Сумма всех элементов: %d\n", sum);

Сумма строки i

int sum = 0;
for (int j = 0; j < m; j++)
{
    sum += a[i][j];
}

Сумма столбца j

int sum = 0;
for (int i = 0; i < n; i++)
{
    sum += a[i][j];
}

Диагонали квадратной матрицы

int main_diag = 0, side_diag = 0;
for (int i = 0; i < n; i++)
{
    main_diag += a[i][i];               /* главная: i == j */
    side_diag += a[i][n - 1 - i];       /* побочная: i + j == n - 1 */
}

Диагонали существуют только у квадратной матрицы (N == M). Если матрица прямоугольная, выводим сообщение — это один из граничных случаев.

Транспонирование

for (int i = 0; i < n; i++)
{
    for (int j = i + 1; j < n; j++)   /* только выше диагонали */
    {
        int t = a[i][j];
        a[i][j] = a[j][i];
        a[j][i] = t;
    }
}

Минимум (максимум) с координатами

int mi = 0, mj = 0;
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        if (a[i][j] < a[mi][mj])
        {
            mi = i;
            mj = j;
        }
    }
}
printf("минимум %d в строке %d, столбце %d\n", a[mi][mj], mi + 1, mj + 1);

Среднее арифметическое

Среднее — дробное число, поэтому нужен double и приведение типа, иначе деление будет целым:

double avg = (double)sum / (n * m);

Граничные случаи

В лабораторной работе отдельно проверяют:

  • матрица 1 × 1 — циклы должны работать без особых условий;
  • прямоугольная матрица (N ≠ M) — диагонали и симметрия только для квадратной;
  • нет искомых элементов (например, нет отрицательных) — выводим понятное сообщение;
  • при вставке и удалении строк/столбцов не выходим за границы массива MAX_SIZE × MAX_SIZE и не забываем менять N или M.

Память и размеры

  • Двумерный массив хранится по строкам: сначала вся строка 0, потом строка 1 и т. д.
  • Поэтому обход «по строкам» (внешний цикл — i, внутренний — j) быстрее, чем наоборот.

Частые ошибки

ОшибкаЧто не так
a[j][i] вместо a[i][j]перепутаны строка и столбец
for (i = 0; i <= n; i++)выход за границы массива
int sum = 0; внутри двойного цикласумма сбрасывается на каждом шаге
(double)sum / (n * m) не поставилицелочисленное деление вместо дробного

Что запомнить

  1. Первый индекс — строка, второй — столбец.
  2. Для обхода нужны два вложенных цикла.
  3. Диагонали и симметрия — только для квадратной матрицы.
  4. Среднее считаем через double.
  5. Программа в ЛР2 обязательно разбивается на функции — об этом в следующей теме.

Подтверждение

—