Указатели и память

Многомерные массивы в C и C++

В этом уроке вы обойдёте двумерную матрицу и найдёте сумму элементов в каждой строке.

Введение

Двумерный массив можно представить как таблицу из строк и столбцов. И в C, и в C++ он хранится в памяти непрерывно, по строкам (row-major): сначала вся первая строка, затем вторая и так далее.

Главное

  • Указатель должен указывать только на существующий объект нужного типа или быть равен null.
  • Каждую выделенную область памяти нужно освободить ровно один раз.
  • Явно обозначайте, какая функция владеет памятью (ownership), а какие данные только читаются (const).

Как это работает

Наведите на ячейку, чтобы увидеть её строку, столбец и место в памяти, где все строки хранятся одна за другой.

Примеры кода на C и C++

C
Запустить →
main.c
#include <stdio.h>

int main(void) {
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    for (int row = 0; row < 2; ++row) {
        int total = 0;
        for (int column = 0; column < 3; ++column) total += matrix[row][column];
        printf("row %d=%d\n", row, total);
    }
    return 0;
}
Результат выполнения
row 0=6
row 1=15
C++
Запустить →
main.cpp
#include <array>
#include <iostream>

int main() {
    std::array<std::array<int, 3>, 2> matrix{{{{1, 2, 3}}, {{4, 5, 6}}}};
    for (std::size_t row = 0; row < matrix.size(); ++row) {
        int total = 0;
        for (int value : matrix[row]) total += value;
        std::cout << "row " << row << '=' << total << '\n';
    }
}
Результат выполнения
row 0=6
row 1=15

Сравнение C и C++

Матрицу обходят два вложенных цикла: внешний проходит по строкам, внутренний складывает элементы текущей строки в переменную total. В C при передаче двумерного массива в функцию нужно указать размеры всех измерений, кроме первого. В C++ std::array хранит размер в самом типе и предлагает метод at(), который проверяет, допустим ли индекс.

C

Владение памятью и её освобождение полностью лежат на программисте.

C++

Контейнеры и RAII автоматически освобождают ресурсы, когда заканчивается время жизни объекта.

Упражнения для практики

Запустите обе версии, а затем измените код, чтобы увидеть разницу в безопасности, которую даёт каждый язык.

  • Проверьте работу с нулевыми указателями и при ошибках выделения памяти.
  • Запустите программу с AddressSanitizer и UndefinedBehaviorSanitizer, чтобы найти ошибки работы с памятью.
  • Явно обозначьте, какой указатель владеет памятью, а какой служит только для чтения.

Попрактикуйтесь на задаче

Примените материал этого урока к реальной задаче. Каждая задача запускается в браузере, с мгновенными тестами и разбором от ИИ.

Все задачи