Все начинается просто и незатейливо: обычный десятиклассник увлекается программированием, знакомится с алгоритмическими задачками, решения которых должны быть быстрыми. Узнает о языке C++, учит минимальный синтаксис, основные конструкции, контейнеры, решает задачи с предопределенным и всегда корректным форматом ввода и вывода, и горя не знает...
В это же время, где-то в большом мире, матерые разработчики каждый день ругают то одни языки программирования, то другие. По самым разным причинам: не удобно, нет какой-то возможности, много лишних букв писать, ошибки в стандартной библиотеке... Но есть язык, который ругают за все и особенно за такую непонятную и таинственную вещь как неопределенное поведение (undefined behavior, UB).
Спустя лет пять или шесть наш простой десятиклассник, горя не видавший в море оторванных от реальности программ, внезапно узнает, что тем самым горячо нелюбимым языком всегда был, остается и будет его C++.
А потом еще в течение нескольких лет он наткнется на самые кошмарные и невероятные ужасы, поджидающие программистов на C++ почти на каждом шагу. Так и появится эта серия заметок, собирающая наиболее отвратительные примеры, на которые очень легко наткнуться при решении повседневных задач.
«Преждевременная оптимизация — корень всех зол» (Д. Кнут или Э. Хоар — в зависимости от того, какой источник смотрите). Язык С++, пожалуй, наиболее яркая тому демонстрация: огромное количество ошибок в C++ программах связаны с неопределенным поведением, заложенным в фундаменте языка просто для того, чтобы дать простор оптимизациям на этапе компиляции.
Если вы собираетесь писать на C++ код, в работоспособности которого хотите быть хоть немного уверенными, стоит знать о существовании различных подводных камней и ловко расставленных мин в стандарте языка, его библиотеке, и всячески их избегать. Иначе ваши программы будут работать правильно только на конкретной машине и только по воле случая.
Важно: этот сборник не является учебным пособием по языку и рассчитан на тех, кто уже знаком с программированием, с C++, и понимает основные его конструкции.
- Что такое UB и как оно проявляется
- Как искать UB?
- Сужающие преобразования
- Целые и вещественные числа
- Нарушения lifetime объектов
- Неработающий синтаксис и стандартная библиотека
- Most Vexing Parse
- Const
- Конструкторы контейнеров
- std::move
- std::enable_if/std::void_t
- Потерянный return
- Эллипсис и функции с произвольным числом аргументов
operator[]
ассоциативных контейнеров- потоки ввода/вывода
operator ,
- function-try-block
- Пустые структуры и типы нулевого размера
- NULL-терминированные строки
- Конструирование std::shared_ptr
- Исполнение программы
- Бесконечные циклы
- Рекурсия
- Ложный noexcept
- Переполнение буфера
- Сборщик мусора
- RAII vs (N)RVO
- Разыменование nullptr
- Static initialization order fiasco
- Static inline
- ODR violation
- Зарезервированные имена
- Тривиальные типы и ABI
- Неинициализированные переменные
- Ranges. Unreachable sentinel
- Невиртуальные виртуальные функции
- Происхождение указателей
- Параллелизм
В тексте могут быть ошибки, опечатки, неточности, он может устаревать. Пожелания, предложения и замечания приветствуются: можно завести issue
или сделать pull request
.
Бегать за вами и судиться автор сего сборника не будет, но все-таки:
На этот проект можно ссылаться. Можно приводить примеры из него, со ссылками, конечно же.
Для копирования и иного воспроизведения надо получить согласие автора
Нельзя использовать в платных сервисах или взимать плату за обучение по этим материалам.
Copyright 2020-2022 Dmitry Sviridkin