Линейный список. Физическое представление. Итераторы: безопасная навигация

Модуль "Линейные списки" • урок 5 из 9 • ~6 минут

Суть урока. Чтобы алгоритмы не зависели от физической реализации списка (массив это или куча), используют итератор - объект-обертку. В языке C он скрывает сырой указатель, чтобы заблокировать опасную адресную арифметику.
Линейный список. Физическое представление. Итераторы: обертка Iterator прячет указатель на узел, Fetch читает данные, Next сдвигает итератор, а конец обхода задает терминатор Last
Итератор дает единый цикл обхода, каким бы ни было физическое устройство

Зачем это нужно?

Итератор необходим для безопасной и универсальной навигации по элементам структуры данных. В языке C указатели можно легко инкрементировать (i++). Это отлично работает для сплошных массивов, но если список динамический и его узлы разбросаны в памяти, операция ptr++ приведет к выходу за пределы памяти и краху программы.

Итератор прячет внутри себя логику перехода к следующему элементу, предоставляя программисту безопасный и единый интерфейс работы.

А как в Python? В Python концепция итераторов встроена в ядро языка. Любой цикл for item in my_list: автоматически создает скрытый объект-итератор и на каждом шаге вызывает у него метод __next__(). Программисту вообще не нужно задумываться о сдвиге указателей - все скрыто за лаконичным синтаксисом.

Линейный список. Физическое представление. Итераторы: как они организованы в C

Итератор специально упаковывается в структуру (struct), внутри которой прячется "сырой" указатель на узел списка. Эта простая упаковка лишает программиста возможности случайно применить встроенную арифметику указателей Си.

/* Сырой узел списка */
typedef struct Item {
    struct Item* prev;
    struct Item* next;
    int data;
} Item;

/* Итератор (обертка для безопасности) */
typedef struct {
    Item* node;
} Iterator;

Абстрактные операции

Управление итератором осуществляется через специальные функции:

Универсальный цикл обхода списка

Вместо перебора по индексам (дискретному времени), обход списка с помощью итератора выглядит так (классическая идиома):

Iterator i;
Iterator last = Last(&list); /* Терминатор (элемент ПОСЛЕ конца списка) */

for (i = First(&list); NotEqual(i, last); i = Next(i)) {
    int value = Fetch(i);
    /* Действие с элементом... */
}

Коротко: главное

  • Итератор - объект, абстрагирующий позицию в структуре данных.
  • В C итератор реализуется как структура-обертка над указателем для защиты от оператора ptr++.
  • Базовые операции: Next, Prev, Fetch (чтение данных), Equal (сравнение).
  • Универсальный цикл обхода не зависит от того, массив внутри или динамические узлы.

Проверьте себя

Зачем в языке C итератор упаковывают в struct, а не передают программисту сырой указатель Item*?

Как вам урок? Спасибо за отзыв!