From 5c76dcd7b9b1f018acb446f2cbfa68fcd657cbdb Mon Sep 17 00:00:00 2001 From: Dmis Date: Sat, 20 Feb 2021 00:40:04 +0300 Subject: [PATCH] typos and comments --- runtime/nullptr_dereference.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/runtime/nullptr_dereference.md b/runtime/nullptr_dereference.md index dec20b5..b08a8fc 100644 --- a/runtime/nullptr_dereference.md +++ b/runtime/nullptr_dereference.md @@ -1,9 +1,9 @@ # Разыменование нулевых указателей. Самая крутая ошибка с самыми жуткими последствиями. `null` вообще называют [ошибкой на миллиард долларов](https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/). -От них страдает куча кода, на самых разных языках программирования. Но если в условной `Java` при обращении по `null`-ссылке вы получите исключение с вполне предсказуемыми последствиями (ну, упало и упало), то в великом и ужасном C++, а также в C за вами придет неопделенное поведение. И оно будет дествительно неопределенным! +От них страдает куча кода, на самых разных языках программирования. Но если в условной `Java` при обращении по `null`-ссылке вы получите исключение с вполне предсказуемыми последствиями (ну, упало и упало), то в великом и ужасном C++, а также в C за вами придет неопределенное поведение. И оно будет дествительно неопределенным! -Но для начала, конечно, надо отметить, что, после всех обсуждений туманных формулировок стандарта, в настоящее время есть некоторое [соглашение](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315), что все-таки не сама по себе конструкция `*p`, где `p` — нулевой указатель, вызывает неопределенное поведение. А `lvalue-to-rvalue` преобразование. Ну или менее формально, кратко и не совсем правильно: пока нет чтения или записи значения, по этому самому нулевому адресу — все нормально. +Но для начала, конечно, надо отметить, что, после всех обсуждений туманных формулировок стандарта, в настоящее время есть некоторое [соглашение](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#315), что все-таки не сама по себе конструкция `*p`, где `p` — нулевой указатель, вызывает неопределенное поведение. А `lvalue-to-rvalue` преобразование. Ну или менее формально, кратко и не совсем правильно: пока нет чтения или записи значения по этому самому нулевому адресу — все нормально. Так, сейчас совершенно законно вы можете вызвать статические методы класса через `nullptr`. @@ -36,9 +36,10 @@ using val_t = decltype(LVALUE(S).foo()); ``` Но, несмотря на то что так делать _можно_, совершенно не значит, что так делать _нужно_. -Потому что последствия от разыменования `nullptr` там, где этого делать нельзя, могут быть печальными. И совершенно не стоит ходить по краю такого лезвия. +Потому что последствия от разыменования `nullptr` там, где этого делать нельзя, могут быть печальными. +Лезвие тонкое, острое, можно легко оступиться и что-нибудь взорвать. -Если разыменовать `nullptr`, может быть вызван код, который никак вызываться [не должен был](https://godbolt.org/z/hPje47): +Если разыменовать `nullptr`, может быть исполнен код, который никак [не вызывался](https://godbolt.org/z/hPje47): ```C++ #include @@ -60,7 +61,7 @@ int main() { } ``` -Компилятор обнаруживат разыменование `nullptr` (вызов функции `Do`). Этого неопределенное поведение. Такого быть не может. Компилятор находит, что есть одно место, где этому указателю присваивается ненулевое значение. А значит именно это значение он и будет использовать. Как результат — вызывается функция, которую мы не вызывали. +Компилятор обнаруживает разыменование `nullptr` (вызов функции `Do`). Это неопределенное поведение. Такого быть не может. Компилятор находит, что есть одно место, где этому указателю присваивается ненулевое значение. И раз нуля быть не может, то, значит, именно это значение он и использует. Как результат — исполняется код функции, которую мы не вызывали. Или вот совершенно дурная программа. ```C++ @@ -97,11 +98,12 @@ void run(int* ptr) { ``` Такая ситуация уже куда ближе к реальности. + В стандартной библиотеке C, например, есть функции, от которых можно было бы, по неопытности, ожидать проверки на `nullptr`, но они этого не делают. -`strlen`, `strcmp`, другие строковые функции, в C++ еще конструктор `std::string(const char*)` — их вызов с `nullptr` в качестве аргумента ведет к неопределенному поведению (и удалению нижерасположенных проверок, если вам не повезет). +`strlen`, `strcmp`, другие строковые функции, а в C++ еще конструктор `std::string(const char*)` — их вызов с `nullptr` в качестве аргумента ведет к неопределенному поведению (и удалению нижерасположенных проверок, если вам не повезет). -Еще есть особо мерзкие в этом смысле `memcpy` и `memmove`. Которые, несмотря на принимаемые в аргументах размеры буферов, все равно приводят к неопределенному поведению, если передать в них `nullptr`! +Еще есть особо мерзкие в этом смысле `memcpy` и `memmove`. Которые, несмотря на принимаемые в аргументах размеры буферов, все равно приводят к неопределенному поведению, если передать в них `nullptr` и нулевой размер! И точно также это может проявиться в удалении ваших проверок. ```C++ @@ -115,7 +117,9 @@ int main(int argc, char **argv) { } char buffer[LENGTH]; - memcpy(buffer, string, length); + memcpy(buffer, string, length); // при передаче nullptr + // length будет нулевым, + // но это не спасает от UB buffer[length] = 0; if (string == NULL) {