Перейти к содержимому
Теория · Ссылки и указатели · урок 5 из 9

Указатель на числа подряд

Вернёмся на улицу памяти. Иногда несколько домов стоят вплотную друг к другу, и в каждом лежит по числу. Чтобы рассказать кому-то про весь ряд, не нужно писать адрес каждого дома. Достаточно двух вещей:

  • бумажки с адресом первого дома;
  • количества домов в ряду.

Дальше человек идёт от первого дома вдоль улицы и считает: нулевой, первый, второй… Так же в C++ функция получает несколько чисел, которые лежат в памяти подряд.

Как получить числа подряд

Запись int data[] = {4, 7, 1}; кладёт три числа int в память одно за другим. Такой ряд называется массивом; подробно массивы разобраны в одной из следующих тем. Когда data передают функции с параметром int*, функция получает указатель на первое число ряда:

C++
#include <iostream>

int sum(int* data, int n) {
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total = total + data[i];
    }
    return total;
}

int main() {
    int data[] = {4, 7, 1};
    std::cout << sum(data, 3) << '\n';  // 12
}

Давайте разберём пример по строкам:

  1. int data[] = {4, 7, 1}; ставит в ряд три дома с числами 4, 7 и 1.
  2. Вызов sum(data, 3) передаёт бумажку с адресом первого дома и количество домов 3.
  3. Внутри sum цикл проходит i от 0 до 2 и складывает data[0], data[1] и data[2].
  4. Программа печатает 12, то есть 4 + 7 + 1.

Шаг вдоль ряда

Если data указывает на первое число, то data + i указывает на число с номером i. Нумерация начинается с нуля, поэтому номера идут от 0 до n - 1. Есть три записи, которые нужно уметь читать:

  • data + i — адрес числа с номером i;
  • data[i] — само число с номером i, короткая форма *(data + i);
  • &data[i] — тоже адрес числа с номером i, то же самое, что data + i.
C++
int data[] = {4, 7, 1};
int* p = data + 2;
std::cout << *p << ' ' << data[2] << '\n';  // 1 1
std::cout << (p == &data[2]) << '\n';  // 1

Разберём по строкам:

  1. int* p = data + 2; отсчитывает от первого дома два шага и записывает на бумажку p адрес числа с номером 2.
  2. *p и data[2] обозначают одно и то же число, поэтому печатается 1 1.
  3. p == &data[2] сравнивает два адреса. Они равны, и std::cout печатает 1, то есть «истина».

Здесь аналогия с улицей заканчивается: в конце ряда нет забора. Улица продолжается чужими домами, и data + n указывает уже за последнее число ряда. Читать data[n] — неопределённое поведение.

Частая ошибка

В цикле написали <= вместо <:

C++
int sum(int* data, int n) {
    int total = 0;
    for (int i = 0; i <= n; ++i) {
        total = total + data[i];  // ✗
    }
    return total;
}

Компилятор не выдаёт ни ошибки, ни предупреждения. Но на последнем шаге цикл читает data[n], то есть число за концом ряда, а это неопределённое поведение. Последний допустимый номер — n - 1, поэтому условие цикла — i < n.

Главное

  • Числа подряд передают функции двумя параметрами: указателем на первое число и их количеством.
  • int data[] = {…} кладёт числа подряд, а data в параметре int* становится указателем на первое из них.
  • data[i] — это *(data + i), а &data[i] — то же, что data + i.
  • Номера идут от 0 до n - 1; чтение data[n] — неопределённое поведение.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом