Учебные материалы: механизм вместо тезисов, блоки «Факты для карточек», Ловушки (Claude Code) + diag/cards_src.tsv
This commit is contained in:
+33
-10
@@ -7,18 +7,26 @@
|
||||
- **Цель:** научиться писать структуру с фиксированной памятью и без сдвигов элементов.
|
||||
- **Почему это в Eltex:** приём/передача пакетов, буферы DMA, очереди между потоками —
|
||||
всё это кольцевые буферы. Понимание wrap-around и «полный/пустой» — прямой вопрос на
|
||||
собеседовании.
|
||||
собеседовании. Механизм тот же, что у сетевой карты: пакеты пишутся в кольцо по мере
|
||||
прихода, драйвер вычитывает их с другого конца, и обе стороны никогда не двигают уже
|
||||
записанные данные по памяти — двигаются только индексы `head`/`tail`.
|
||||
- **Что сдаётся:** `solution.cpp`, проходящий `python3 grade.py 03`.
|
||||
|
||||
## Глава II. Что нужно знать до старта
|
||||
|
||||
Идея: массив фиксированного размера + два индекса. `head` — куда писать, `tail` — откуда
|
||||
читать. Когда индекс доходит до конца, он возвращается в начало: `idx = (idx + 1) % capacity`.
|
||||
Сдвигать элементы не нужно никогда — в этом весь смысл.
|
||||
Сдвигать элементы не нужно никогда — в этом весь смысл: `push`/`pop` двигают только индекс,
|
||||
а не байты в памяти, поэтому обе операции остаются O(1) независимо от размера буфера.
|
||||
|
||||
Ловушка, из-за которой задача попадает в собеседования: **как отличить пустой буфер от
|
||||
полного, если оба индекса совпали?** Варианты: хранить счётчик `size`, либо оставлять одну
|
||||
ячейку свободной. В нашем интерфейсе есть `size()`, поэтому проще хранить счётчик.
|
||||
полного, если оба индекса совпали?** При `head == tail` возможны оба состояния — буфер мог
|
||||
быть только что создан (пуст) или заполнен ровно `capacity` раз (полон) — по одним индексам
|
||||
это не различить. Варианты: хранить счётчик `size`, либо оставлять одну ячейку свободной.
|
||||
В нашем интерфейсе есть `size()`, поэтому проще хранить счётчик.
|
||||
|
||||
Почему дальше: тот же счётчик `size_` вместо пересчёта по индексам — частый приём и в
|
||||
`std::deque`, и в реализациях lock-free очередей, где индексы вообще нельзя лишний раз читать.
|
||||
|
||||
## Глава III. Задание
|
||||
|
||||
@@ -89,14 +97,29 @@ public:
|
||||
`std::vector<int>` — тогда вопрос исчезает.
|
||||
</details>
|
||||
|
||||
## Глава VII. Частые ошибки
|
||||
## Глава VII. Ловушки
|
||||
|
||||
- Сдвиг элементов вместо индексов (теряется весь смысл O(1)).
|
||||
- Путаница «пусто/полно» при совпавших индексах.
|
||||
- Сдвиг элементов вместо индексов → цена `push`/`pop` вырастает до O(n) → теряется весь
|
||||
смысл структуры, видно по сравнению с наивным `std::vector` на бенчмарке.
|
||||
- Путаница «пусто/полно» при совпавших индексах (`head_ == tail_`) без отдельного `size_` →
|
||||
`full()` и `empty()` дают одинаковый ответ на разных состояниях → тест на заполненный буфер
|
||||
ёмкости > 1 падает.
|
||||
- `%` на каждом шаге там, где можно было обойтись условием — не ошибка, но на собеседовании
|
||||
спросят «а быстрее можно?» (ответ: `if (++idx == cap) idx = 0;`).
|
||||
- Копирование объекта без правила трёх → двойное освобождение. Если сдаёшь с ручным `new[]`,
|
||||
запрети копирование (`= delete`).
|
||||
спросят «а быстрее можно?» (ответ: `if (++idx == cap) idx = 0;`, деление по модулю дороже
|
||||
сравнения).
|
||||
- Копирование объекта без правила трёх/пяти → два объекта владеют одним и тем же `new[]`-буфером
|
||||
→ двойное освобождение при выходе обоих из области видимости, ловится ASAN. Если сдаёшь с
|
||||
ручным `new[]`, запрети копирование (`= delete`).
|
||||
|
||||
**Факты для карточек**
|
||||
- base | Сложность `push`/`pop` кольцевого буфера? — O(1)
|
||||
- core | Как отличить пустой буфер от полного при `head_ == tail_`? — хранить отдельный
|
||||
счётчик `size_` (или жертвовать одной ячейкой)
|
||||
- core | Чем `delete[]` отличается от `delete` для массива, выделенного `new[]`? — `delete`
|
||||
без `[]` на массиве — UB, вызовет деструктор только для первого элемента и испортит подсчёт
|
||||
размера аллокации
|
||||
- base | Какое условие делает `push` дешевле, чем `% capacity_` на каждый вызов? —
|
||||
`if (++idx == cap) idx = 0;`
|
||||
|
||||
## После сдачи
|
||||
|
||||
|
||||
Reference in New Issue
Block a user