Skip to content

Nekrolm/ubbook

Repository files navigation

Путеводитель C++ программиста по неопределенному поведению

If you are looking for english version

Паникуй!


Коротко о том, зачем и почему

Все начинается просто и незатейливо: обычный десятиклассник увлекается программированием, знакомится с алгоритмическими задачками, решения которых должны быть быстрыми. Узнает о языке C++, учит минимальный синтаксис, основные конструкции, контейнеры, решает задачи с предопределенным и всегда корректным форматом ввода и вывода, и горя не знает...

В это же время, где-то в большом мире, матерые разработчики каждый день ругают то одни языки программирования, то другие. По самым разным причинам: не удобно, нет какой-то возможности, много лишних букв писать, ошибки в стандартной библиотеке... Но есть язык, который ругают за все и особенно за такую непонятную и таинственную вещь как неопределенное поведение (undefined behavior, UB).

Спустя лет пять или шесть наш простой десятиклассник, горя не видавший в море оторванных от реальности программ, внезапно узнает, что тем самым горячо нелюбимым языком всегда был, остается и будет его C++.

А потом еще в течение нескольких лет он наткнется на самые кошмарные и невероятные ужасы, поджидающие программистов на C++ почти на каждом шагу. Так и появится эта серия заметок, собирающая наиболее отвратительные примеры, на которые очень легко наткнуться при решении повседневных задач.


«Преждевременная оптимизация — корень всех зол» (Д. Кнут или Э. Хоар — в зависимости от того, какой источник смотрите). Язык С++, пожалуй, наиболее яркая тому демонстрация: огромное количество ошибок в C++ программах связаны с неопределенным поведением, заложенным в фундаменте языка просто для того, чтобы дать простор оптимизациям на этапе компиляции.

Если вы собираетесь писать на C++ код, в работоспособности которого хотите быть хоть немного уверенными, стоит знать о существовании различных подводных камней и ловко расставленных мин в стандарте языка, его библиотеке, и всячески их избегать. Иначе ваши программы будут работать правильно только на конкретной машине и только по воле случая.

В этой книге я собрал множество самых разных примеров как в коде на C и C++ можно наткнуться на неопределенное, неожиданное и совершенно ошибочное поведение. И хотя основной фокус книги всё же на неопределенном поведении, в некоторых разделах описываются вещи вполе специфицированные, но довольно неочевидные.

Важно: этот сборник не является учебным пособием по языку и рассчитан на тех, кто уже знаком с программированием, с C++, и понимает основные его конструкции.


Содержание

  1. Что такое UB и как оно проявляется
  2. Как искать UB?
  3. Целые и вещественные числа
    1. Сужающие преобразования
    2. Переполнение знаковых целых чисел
    3. Числа с плавающей точкой
    4. Integer promotion
    5. char и знаковое расширение
    6. Унарный минус для беззнаковых чисел
  4. Нарушения lifetime объектов
    1. Висячие ссылки — общие случаи
    2. Автовывод типов и висячие ссылки
    3. std::string_view
    4. Range-based for
    5. Cамоинициализация
    6. std::vector и инвалидация ссылок
    7. Висячие ссылки в лямбдах
    8. Создание кортежей
    9. Внезапная мутабельность
    10. Proxy-объекты и ссылки
    11. use-after-move
    12. lifetime extension
    13. C++20 direct initialization и ссылочные поля
    14. Тернарный оператор
    15. Корутины и время жизни
  5. (Не)работающий синтаксис
    1. Most Vexing Parse
    2. Const
    3. std::move
    4. Потерянный return
    5. Эллипсис и функции с произвольным числом аргументов
    6. operator ,
    7. function-try-block
    8. Пустые структуры и типы нулевого размера
    9. (Не)явное приведение типов
    10. Многомерный operator[]
    11. Операторы сравнения в C++20
    12. Атрибут [[assume]]
    13. Конструкторы по умолчанию и = default
    14. implicit bool
  6. Стандартная библиотека
    1. NULL-терминированные строки
    2. Конструирование std::shared_ptr
    3. shared_from_this
    4. потоки ввода/вывода
    5. std::aligned_storage
    6. функции стантарной библиотеки как параметры
    7. std::ranges::views
    8. operator[] ассоциативных контейнеров
    9. std::enable_if/std::void_t
    10. Конструкторы контейнеров
    11. std::uniform_int_distribution
    12. std::ranges::transform | filter
    13. vector::reserve и vector::resize
    14. std::function
    15. std::forward
  7. Исполнение программы
    1. Бесконечные циклы
    2. Рекурсия
    3. Ложный noexcept
    4. Переполнение буфера
    5. Сборщик мусора
    6. RAII vs (N)RVO
    7. Разыменование nullptr
    8. Static initialization order fiasco
    9. Static inline
    10. ODR violation
    11. Зарезервированные имена
    12. Тривиальные типы и ABI
    13. Неинициализированные переменные
    14. Ranges. Unreachable sentinel
    15. Невиртуальные виртуальные функции
    16. Variable length array
    17. ODR violation и разделяемые библиотеки
    18. Владение и исключения
  8. Происхождение указателей
    1. Невалидные указатели
    2. Placement operator new[]
    3. Невыровненные ссылки
    4. strict aliasing
  9. Асинхронность и параллелизм
    1. Race condition
    2. shared_ptr
    3. thread::join
    4. Повторный захват mutex
    5. Signal-unsafe
    6. condition_variable
    7. Гонки за vptr
    8. std::async
    9. Файловая система

Помощь

В тексте могут быть ошибки, опечатки, неточности, он может устаревать. Пожелания, предложения и замечания приветствуются: можно завести issue или сделать pull request.

И еще кое-что

Бегать за вами и судиться автор сего сборника не будет, но все-таки:

На этот проект можно ссылаться. Можно приводить примеры из него, со ссылками, конечно же.

Для копирования и иного воспроизведения надо получить согласие автора

Нельзя использовать в платных сервисах или взимать плату за обучение по этим материалам.

Ну и самое последнее примечание

Черновое название этой работы, "Ружье достаточной огневой мощи, чтобы на нем повеситься", как могли догадаться искушенные читатели, было эдаким реверансом в сторону известного (но очень плохо состарившегося) сборника по C++ "Веревка достаточной длины, чтобы выстрелить себе в ногу" от Алана Голуба. Но, к сожалению, мы живем в нежном мире победивших алгоритмов ранжирования и надзорных органов, то и дело стремящихся кого-нибудь от чего-нибудь защитить.

Автор, конечно, очень бы хотел защитить всех от C++, и именно этому и служит данных сборник, но с заблокированным и пессимизированным репозиторием прогресса в этом направлении не будет.

Copyright 2020-2024 Dmitry Sviridkin