From 8c1499929a5534991e291c9a75d341a58c791f9b Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Mon, 29 Sep 2025 17:13:45 +0300 Subject: [PATCH] Fix a number of typos (#128) Co-authored-by: Sergey Fukanchik --- README.md | 2 +- concurrency/filesystem.md | 4 ++-- concurrency/race_condition.md | 4 ++-- concurrency/std_async.md | 2 +- lifetime/coroutines_and_lifetimes.md | 4 ++-- lifetime/direct_initialization_references.md | 6 +++--- lifetime/ternary_operator.md | 2 +- numeric/char_sign_extension.md | 4 ++-- numeric/overflow.md | 2 +- runtime/dll_and_odr_violation.md | 6 +++--- runtime/ownership_and_exceptions.md | 2 +- runtime/virtual_functions.md | 2 +- runtime/vla.md | 6 +++--- .../function_pass_and_address_restriction.md | 6 +++--- standard_lib/null_terminated_string.md | 2 +- standard_lib/shared_from_this.md | 6 +++--- standard_lib/std_function_const.md | 10 +++++----- standard_lib/transform_filter_ranges.md | 18 +++++++++--------- standard_lib/vector_resize_reserve.md | 2 +- syntax/assume.md | 6 +++--- syntax/c_variadic.md | 4 ++-- syntax/const_launder.md | 6 +++--- syntax/explicit_but_implicit.md | 2 +- syntax/implicit_bool.md | 8 ++++---- syntax/missing_return.md | 2 +- syntax/most_vexing_parse.md | 6 +++--- syntax/multidimensional_subscript.md | 2 +- 27 files changed, 63 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 64d2390..2fbbcd0 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Если вы собираетесь писать на C++ код, в работоспособности которого хотите быть хоть немного уверенными, стоит знать о существовании различных подводных камней и ловко расставленных мин в стандарте языка, его библиотеке, и всячески их избегать. Иначе ваши программы будут работать правильно только на конкретной машине и только по воле случая. -В этой книге я собрал множество самых разных примеров как в коде на C и C++ можно наткнуться на неопределенное, неожиданное и совершенно ошибочное поведение. И хотя основной фокус книги всё же на неопределенном поведении, в некоторых разделах описываются вещи вполе специфицированные, но довольно неочевидные. +В этой книге я собрал множество самых разных примеров как в коде на C и C++ можно наткнуться на неопределенное, неожиданное и совершенно ошибочное поведение. И хотя основной фокус книги всё же на неопределенном поведении, в некоторых разделах описываются вещи вполне специфицированные, но довольно неочевидные. **Важно:** этот сборник **не является учебным пособием** по языку и рассчитан на тех, кто уже знаком с программированием, с C++, и понимает основные его конструкции. diff --git a/concurrency/filesystem.md b/concurrency/filesystem.md index ea01167..54a329b 100644 --- a/concurrency/filesystem.md +++ b/concurrency/filesystem.md @@ -7,7 +7,7 @@ И почти всегда это нежелательная, ошибочная ситуация, последствия которой варьируются от просто неправильного результата до чудовищных уязвимостей в безопасности. -Иногда, конечно, система может быть толерантна к таким ошибкам и серьезных проблем не возникнет: например, если страничка магазина запрашивает список товаров на складе, а в этот момент база данных склада обновляется, то вы, как пользователь, возможно, увидите неполный или устаревший список товаров или смесь из старых и новых данных. Но критического ничего не произойдет, если разработчики предусмотрели дополнительные проверки с запросом к базе данных в момент взаимодейтсвия с конкретным товаром из списка... Чтобы вы не купили то, чего больше не существует. +Иногда, конечно, система может быть толерантна к таким ошибкам и серьезных проблем не возникнет: например, если страничка магазина запрашивает список товаров на складе, а в этот момент база данных склада обновляется, то вы, как пользователь, возможно, увидите неполный или устаревший список товаров или смесь из старых и новых данных. Но критического ничего не произойдет, если разработчики предусмотрели дополнительные проверки с запросом к базе данных в момент взаимодействия с конкретным товаром из списка... Чтобы вы не купили то, чего больше не существует. Файловая система — один из таких ресурсов, гонки за которым естественны и должны учитываться при разработке приложений. Да, самые низкоуровневые проблемы синхронизации чтения и записи в файловую систему берет на себя операционная система и (или) конкретный драйвер. Можно «спокойно» одновременно из разных процессов читать и писать в один и тот же файл и получать мусор или штатные ошибки: низкоуровневые операции `read` и `write` будут как-то упорядочены планировщиком ввода-вывода. С самой файловой системой при этом всё будет в порядке. @@ -33,7 +33,7 @@ int main() { Это класcическая **TOCTOU** (*Time-of-Check-Time-of-Use*) ошибка. Между проверкой и открытием файла, файл может быть удален. -Но причем тут С++, если такая проблема существет для любого языка программирования? +Но причем тут С++, если такая проблема существует для любого языка программирования? Действительно. Например, во всех версиях стандартной библиотеки Rust с 1.0 до 1.58.1 [была](https://blog.rust-lang.org/2022/01/20/cve-2022-21658.html) похожая ошибка в реализации функции `remove_dir_all`. diff --git a/concurrency/race_condition.md b/concurrency/race_condition.md index 5712db2..2829680 100644 --- a/concurrency/race_condition.md +++ b/concurrency/race_condition.md @@ -6,10 +6,10 @@ Так почему бы не взять какую-нибудь готовую серьезную библиотеку (`boost`, `abseil`) — там наверняка умные люди уже пострадали многие часы, чтобы предоставить удобные и безопасные инструменты — и забот не знать?! -Увы, так не работает. Правильность использования этих инструментов в C++ нужно контроллировать самостоятельно, пристально изучая каждую строчку кода. +Увы, так не работает. Правильность использования этих инструментов в C++ нужно контролировать самостоятельно, пристально изучая каждую строчку кода. Мы все равно втыкаемся в проблемы синхронизации доступа, с аккуратным развешиванием мьютексов и атомарных переменных. -Ситуация (_data race_), в которой один поток программы модифицирует объект, а другой, в то же самое время, читает значения из этого объекта — или просто два потока одновременно пытаются модифицировать один объект — совершенно ясно, является ошибочной. Результат чтения может выдать какой-то странный промежуточный объект. Совместная запись — породить какое-то мутировавшее премешаное значение. Независимо от языка программирования. +Ситуация (_data race_), в которой один поток программы модифицирует объект, а другой, в то же самое время, читает значения из этого объекта — или просто два потока одновременно пытаются модифицировать один объект — совершенно ясно, является ошибочной. Результат чтения может выдать какой-то странный промежуточный объект. Совместная запись — породить какое-то мутировавшее перемешанное значение. Независимо от языка программирования. Но в C++ это не просто ошибка. Это неопределенное поведение. И «возможности» для оптимизации diff --git a/concurrency/std_async.md b/concurrency/std_async.md index c2efc5b..48153f6 100644 --- a/concurrency/std_async.md +++ b/concurrency/std_async.md @@ -129,7 +129,7 @@ elapsed: 100 - Переданная функция каким-то образом начнет исполнятся асинхронно (в фоновом потоке или в пуле потоком ­— это тоже не специфировано). И такое поведение по умолчанию во всех современных версиях GCC, Clang и MSVC. - Переданная функция не будет выполнятся до тех пор, пока вы не вызовете `wait` или `get` у возвращенной `std::future`. И такое поведение долгое время было со старыми версиями компиляторов. Например, [GCC 5.4](https://gcc.godbolt.org/z/nY6Kv4Gdz) -Какое именно поведение вы хотите можно и нужно контроллировать с помощью вызова перегрузки `std::async` с дополнительным первым параметром типа `std::launch`: +Какое именно поведение вы хотите можно и нужно контролировать с помощью вызова перегрузки `std::async` с дополнительным первым параметром типа `std::launch`: - `std::launch::async` — если вы действительно хотите асинхронное исполнение - `std::launch::deferred` — если нужно отложить до точки вызова `wait` или `get` diff --git a/lifetime/coroutines_and_lifetimes.md b/lifetime/coroutines_and_lifetimes.md index c7d92b1..9b1b205 100644 --- a/lifetime/coroutines_and_lifetimes.md +++ b/lifetime/coroutines_and_lifetimes.md @@ -137,7 +137,7 @@ awaitable handle_request(const Request& r) Корутины — очень сложные объекты, которые обманчиво просты в использовании из-за синтаксического сахара. В этом же весь смысл! Поддержка `async/await` на уровне языка и компиляторов делает простым то, что всегда было делать сложно вручную... Так в происходит в высокоуровневых и безопасных языках с автоматическим управлением памятью: Python, C#, JavaScript, Kotlin. Но не в C++. И не в Rust. (И не в Zig). -В примере выше есть как минимуи **три** точки отказа, содержащих ошибки. Можете подумать об этом, пока мы будем разворачивать проблемы корутин С++. +В примере выше есть как минимум **три** точки отказа, содержащих ошибки. Можете подумать об этом, пока мы будем разворачивать проблемы корутин С++. ### Что такое корутина? @@ -635,7 +635,7 @@ batch_processor 15ms ``` ------ -Отслеживать время жизни ссылок в асинхронном коде вручную крайне тяжело. Автоматика, как в Rust, делает это намного лучше, но при этом может выдавать совершенно непонятные репорты, в которых можно разобраться только если знаешь, что именно могло пойти не так — за это async в Rust и не любят и критикуют. А в качестве самого простого и продуктивного решения, чтоб убложить borrow checker, выбирается копирование всего подряд (`.clone()`, везде `.clone()`) +Отслеживать время жизни ссылок в асинхронном коде вручную крайне тяжело. Автоматика, как в Rust, делает это намного лучше, но при этом может выдавать совершенно непонятные репорты, в которых можно разобраться только если знаешь, что именно могло пойти не так — за это async в Rust и не любят и критикуют. А в качестве самого простого и продуктивного решения, чтоб ублажить borrow checker, выбирается копирование всего подряд (`.clone()`, везде `.clone()`) С++ отдает полный контроль вам с невероятной кучей неявных захватов ссылок! Делайте с ними что хотите и как хотите. Скомпилируется без проблем и проверок. Вы можете приложить ментальные усилия, отследить все ссылки и убедиться что объекты не умрут не вовремя. Либо можно отчаяться, прочитать гайдлайны и передавать все и всегда by value. Копируя и перемещая. Никаких ссылок. diff --git a/lifetime/direct_initialization_references.md b/lifetime/direct_initialization_references.md index e2da2aa..baa6a34 100644 --- a/lifetime/direct_initialization_references.md +++ b/lifetime/direct_initialization_references.md @@ -31,7 +31,7 @@ auto p = std::make_unique(1, 5); ```C++ // https://godbolt.org/z/fTM4n7nqz -auto p = std::make_unique(1, 5); // теперь компилирутся в C++20 +auto p = std::make_unique(1, 5); // теперь компилируется в C++20 ``` Ну добавили и добавили... Стало же удобнее? Можно теперь всегда круглые скобки использовать?... @@ -90,7 +90,7 @@ struct Widget { }; // Эта строчка после вашей оптимизации продолжает молча компилироваться -// но теперь влечет неопределенное поведенине +// но теперь влечет неопределенное поведение // пример: https://gcc.godbolt.org/z/q73erhYWs auto parent_widget = std::make_unqiue(read_config()); // И статические анализаторы пока молчат https://gcc.godbolt.org/z/aMsT3afxb @@ -119,7 +119,7 @@ int main() { ## Значения по умолчанию для ссылочных полей -Закончить, пожалуй, нужно еще одним недоразуменинем с ссылочными полями. Им же можно задать значения по умолчанию... +Закончить, пожалуй, нужно еще одним недоразумением с ссылочными полями. Им же можно задать значения по умолчанию... ```C++ struct Config { diff --git a/lifetime/ternary_operator.md b/lifetime/ternary_operator.md index 77563aa..191d8be 100644 --- a/lifetime/ternary_operator.md +++ b/lifetime/ternary_operator.md @@ -139,7 +139,7 @@ test_default_getter(m, 42, []{ return "default"sv; }); ``` `const string& : string_view` — первый неявно приводим ко второму. Взятие string_view от string происходит без копирования. Все отлично... И вроде безопасно. -А что если наша таблица с конфигурацией отпимизирована хранить string_view на части одной большой json-конфигурационной строки и ключа в ней нет? +А что если наша таблица с конфигурацией оптимизирована хранить string_view на части одной большой json-конфигурационной строки и ключа в ней нет? ```C++ using RefMap = std::map; diff --git a/numeric/char_sign_extension.md b/numeric/char_sign_extension.md index da8400d..7a6d7a2 100644 --- a/numeric/char_sign_extension.md +++ b/numeric/char_sign_extension.md @@ -19,7 +19,7 @@ struct CharTable { }; ``` -Все ли впорядке с этим безобидным методом `is_whitespace`? Ну кроме того, что `char` в C/C++ обычно восьмибитный, а в unicode [есть](https://jkorpela.fi/chars/spaces.html) пробельные символы, кодируемые 16 битами. +Все ли в порядке с этим безобидным методом `is_whitespace`? Ну кроме того, что `char` в C/C++ обычно восьмибитный, а в unicode [есть](https://jkorpela.fi/chars/spaces.html) пробельные символы, кодируемые 16 битами. Давайте [потестируем](https://godbolt.org/z/75rTW1nMG) @@ -89,4 +89,4 @@ reference operator[]( size_type pos ); 1. https://en.cppreference.com/w/cpp/language/types 2. https://en.cppreference.com/w/cpp/container/array/operator_at 3. https://en.cppreference.com/w/cpp/types/climits -4. https://docs.oracle.com/cd/E19205-01/819-5265/bjamz/index.html \ No newline at end of file +4. https://docs.oracle.com/cd/E19205-01/819-5265/bjamz/index.html diff --git a/numeric/overflow.md b/numeric/overflow.md index fb84d35..4e1ed95 100644 --- a/numeric/overflow.md +++ b/numeric/overflow.md @@ -146,7 +146,7 @@ usummate_squares(unsigned long): # @usummate_squares(unsigned l */ ``` -GCC 13 на данный момент *(2024 год)* в принципе [не делает](https://godbolt.org/z/xjf7zj768) таких оптимизаций по умолчанию. При этом последнии версии Clang 18 +GCC 13 на данный момент *(2024 год)* в принципе [не делает](https://godbolt.org/z/xjf7zj768) таких оптимизаций по умолчанию. При этом последние версии Clang 18 уже способны свернуть цикл суммирования квадратов и для беззнаковых: ```asm diff --git a/runtime/dll_and_odr_violation.md b/runtime/dll_and_odr_violation.md index 6c97bbc..ad4e61a 100644 --- a/runtime/dll_and_odr_violation.md +++ b/runtime/dll_and_odr_violation.md @@ -1,10 +1,10 @@ # ODR-violation и разделяемые библиотеки -[Ранее](odr_violation.md) я рассматривал ODR-violation в общих чертах и предупреждал о том, что может произойти, если случайно выбрать не то имя переменной, структуры или функции в C++. В этой же части я бы хотел продемострировать более изящный пример, не требующий приложения никаких усилий по написанию кривого кода. Достаточно просто иметь кривой код в ваших third-party зависимостях. +[Ранее](odr_violation.md) я рассматривал ODR-violation в общих чертах и предупреждал о том, что может произойти, если случайно выбрать не то имя переменной, структуры или функции в C++. В этой же части я бы хотел продемонстрировать более изящный пример, не требующий приложения никаких усилий по написанию кривого кода. Достаточно просто иметь кривой код в ваших third-party зависимостях. Недавно я имел дело со странным баг-репортом: -Во внутренем репозитории с пакетами обновилcя пакет с библиотекой [gtest](https://github.com/google/googletest) -- известная уважаемая библиотека для написания самых разных тестов на C++. И в результате обновления некоторые тесты в конечных приложениях стали внезапно падать. +Во внутреннем репозитории с пакетами обновилcя пакет с библиотекой [gtest](https://github.com/google/googletest) -- известная уважаемая библиотека для написания самых разных тестов на C++. И в результате обновления некоторые тесты в конечных приложениях стали внезапно падать. Падать они стали по-разному. У одних стали валиться проверяющие ассерты. У других же все работало, проверки проходили, но [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) рапортовал что тестирующий процесс вышел с ненулевым кодом возврата. @@ -190,7 +190,7 @@ Segmentation fault (core dumped) ``` Ура! Падает! -Обе библиотеки имеют свою собсвенную версию глобальной переменной с одним и тем же неявно экспортируемым именем. Использоваться опять-таки будет только **одна**. +Обе библиотеки имеют свою собственную версию глобальной переменной с одним и тем же неявно экспортируемым именем. Использоваться опять-таки будет только **одна**. После загрузки библиотеки, после конструирования глобальной переменной, стандарт C++ требует зарегистрировать (например через `__cxa_atexit`) функцию для вызова деструктора. У нас две библиотеки -- значит две функции будут вызваны. На одном и том же объекте. Double free. Конструктор, кстати, также вызывается дважды по одному и тому же адресу: ```C++ diff --git a/runtime/ownership_and_exceptions.md b/runtime/ownership_and_exceptions.md index 441aa89..bf4eb97 100644 --- a/runtime/ownership_and_exceptions.md +++ b/runtime/ownership_and_exceptions.md @@ -2,7 +2,7 @@ Команде однажды завели баг-репорт: "Сервис упал c segmentation fault, в core dump стэк-трейс указывает как последнюю функцию перед падением что-то из вашей библиотеки. Разберитесь!". Упал сервис ровно один раз за полгода. -Этим чем-то был вызов `free` где-то глубоко-глубоко внутри библиотери Protobuf. И несколько последующих стэк-фреймов указывали на вызов деструктора уже в нашей библиотеке. Потратив некоторое время на анализ кода деструктора, дежурный инженер не нашел ничего подозрительного и предположил, что это похоже на какую-то ранее встреченную проблему в Protobuf. И как воспроизвести никто не представлял. Тупик... +Этим чем-то был вызов `free` где-то глубоко-глубоко внутри библиотеки Protobuf. И несколько последующих стэк-фреймов указывали на вызов деструктора уже в нашей библиотеке. Потратив некоторое время на анализ кода деструктора, дежурный инженер не нашел ничего подозрительного и предположил, что это похоже на какую-то ранее встреченную проблему в Protobuf. И как воспроизвести никто не представлял. Тупик... Я заинтересовался этой загадочной историей и залез в core dump поглубже. diff --git a/runtime/virtual_functions.md b/runtime/virtual_functions.md index 451b6f8..55a2b04 100644 --- a/runtime/virtual_functions.md +++ b/runtime/virtual_functions.md @@ -13,7 +13,7 @@ public: }; ``` -Пользователи интерфейса нареализовывали свойх имплементаций. Все были счастливы, пока кто-то не сделал асинхронную реализацию. С ней почему-то приложение стало падать. Проведя небольшое расследование, вы выяснили, что пользователи интерфейса не позаботились вызвать метод `stop()` перед разрушением объекта. Какая досада! +Пользователи интерфейса нареализовывали своих имплементаций. Все были счастливы, пока кто-то не сделал асинхронную реализацию. С ней почему-то приложение стало падать. Проведя небольшое расследование, вы выяснили, что пользователи интерфейса не позаботились вызвать метод `stop()` перед разрушением объекта. Какая досада! Вы были уставши и злы. А быть может это были и не вы, а какой-то менее опытный коллега, которому поручили доработать интерфейс. В общем, на свет родилась правка diff --git a/runtime/vla.md b/runtime/vla.md index bf08165..0ae5602 100644 --- a/runtime/vla.md +++ b/runtime/vla.md @@ -11,7 +11,7 @@ int32_t foo(int32_t x, int32_t y) { ``` При вызове функции `foo` на стеке, после аргументов (хотя не факт что аргументы будут переданы через стек), будет выделено (просто вершина стека будет сдвинута) еще 4 байта (а может быть и больше, кто ж знает, что там настроено у компилятора!) под переменную z. А может быть и не будет выделено (например, если компилятор оптимизирует переменную и сложит результат сразу в регистр `rax`). Дикая природа удивительна, неправда ли? -Освобождается память со стека тоже автоматически. Причем уже не обычно, а всегда. Если только, конечно, вы случайно не сломали стек, не сделали чудовищную ассемблерную вставку и теперь адрес возврата не ведет куда-то не туда или не используете `attribute ( ( naked ) )`. Но, мне кажется, в этих случаях у вас куда более серьезные проблемы... Во всех остальных случаях память со стека освобожается автоматически. Потому, как известно, вот такой код порождает висячий указатель +Освобождается память со стека тоже автоматически. Причем уже не обычно, а всегда. Если только, конечно, вы случайно не сломали стек, не сделали чудовищную ассемблерную вставку и теперь адрес возврата не ведет куда-то не туда или не используете `attribute ( ( naked ) )`. Но, мне кажется, в этих случаях у вас куда более серьезные проблемы... Во всех остальных случаях память со стека освобождается автоматически. Потому, как известно, вот такой код порождает висячий указатель ```C int32_t* foo(int32_t x, int32_t y) { @@ -78,7 +78,7 @@ int main() { Тут каждый вызов `alloca` не приводит к переполнению стека сам по себе. Но если `use_alloca` будет заинлайнена компилятором по какой-либо причине, мы получим [SIGSEGV](https://godbolt.org/z/1xWsjqK4G) -Использование `alloca` и VLA крайне не рекомендуется. [man](https://man7.org/linux/man-pages/man3/alloca.3.html) упоминает случай, когда их использование может быть оправдано: ваш код полагается на setjmp/longjump и нормальный менеджмент динамически выделенной памяти можеь быть осложнен, а стек все равно будет очищен даже при longjmp. Не буду спрашивать, зачем оно вам... +Использование `alloca` и VLA крайне не рекомендуется. [man](https://man7.org/linux/man-pages/man3/alloca.3.html) упоминает случай, когда их использование может быть оправдано: ваш код полагается на setjmp/longjump и нормальный менеджмент динамически выделенной памяти может быть осложнен, а стек все равно будет очищен даже при longjmp. Не буду спрашивать, зачем оно вам... alloca и vla действительно в среднем быстрее чем динамическая аллокация. Но если уж нужно действительно быстро, то вариант с преаллоцированным массивом или массивом фиксированной длины [получше будет](https://quick-bench.com/q/JWSPzPFknaSECE2W1fPiQvnEdGs) @@ -106,4 +106,4 @@ int main(int argc, char* argv[]) { 1. https://lwn.net/Articles/749064/ 2. https://man7.org/linux/man-pages/man3/alloca.3.html 3. https://nullprogram.com/blog/2019/10/27/ -4. https://en.cppreference.com/w/c/language/array \ No newline at end of file +4. https://en.cppreference.com/w/c/language/array diff --git a/standard_lib/function_pass_and_address_restriction.md b/standard_lib/function_pass_and_address_restriction.md index fd325f6..96a261c 100644 --- a/standard_lib/function_pass_and_address_restriction.md +++ b/standard_lib/function_pass_and_address_restriction.md @@ -129,14 +129,14 @@ auto f = [](float a) -> float { int main() { return integrate((float(*)(float))(f)); - // комилируется и работает + // компилируется и работает } // ----------------------------- // https://godbolt.org/z/fqzdse1Ya -// ниблоиды в std чаще определяются так, а не с помощью лямбл +// ниблоиды в std чаще определяются так, а не с помощью лямбд struct { static float operator()(float x) { return x; @@ -210,7 +210,7 @@ float integrate(float (*)(float)): pxor xmm0, xmm0 cvtsi2ss xmm0, ebx add ebx, 1 - call rbp // ! нет информации о функии -- вызов по указателю + call rbp // ! нет информации о функции -- вызов по указателю addss xmm0, DWORD PTR [rsp+12] movss DWORD PTR [rsp+12], xmm0 cmp ebx, 26 diff --git a/standard_lib/null_terminated_string.md b/standard_lib/null_terminated_string.md index bc65943..e7613be 100644 --- a/standard_lib/null_terminated_string.md +++ b/standard_lib/null_terminated_string.md @@ -127,7 +127,7 @@ NULL-терминированные строки -- унаследованная В новых C-библиотеках стараться проектировать API, использующие пару указатель + длина, а не только лишь указатель на NULL-терминированную последовательность. От этого наследия страдают программы на всех языках, вынужденные взаимодействовать с C API. -Rust, например, использует отдельные типы `CStr` и `CString` для подобных строк и переход к ним из нормального кода всегда сопровоздается мучительными тяжелыми преобразованиями. +Rust, например, использует отдельные типы `CStr` и `CString` для подобных строк и переход к ним из нормального кода всегда сопровождается мучительными тяжелыми преобразованиями. Использование NULL-терминатора встречается не только для текстовых строк. Так, например, библиотека [SRILM](http://www.speech.sri.com/projects/srilm/) активно использует 0-терминированные последовательности числовых идентификаторов, создавая этим дополнительные проблемы. Семейство функций exec в Linux принимают NULL-терминированные последовательности указателей. diff --git a/standard_lib/shared_from_this.md b/standard_lib/shared_from_this.md index e3e2335..2059ad9 100644 --- a/standard_lib/shared_from_this.md +++ b/standard_lib/shared_from_this.md @@ -83,7 +83,7 @@ free(): invalid pointer Program terminated with signal: SIGSEGV ``` -`std::shared_ptr