Как объяснить неопределенное поведение новичков-новичков?

Существует несколько ситуаций, которые стандарт C ++ определяет как неопределенное поведение. Например, если я выделяю new[] , а затем попытаюсь освободить с помощью delete (not delete[] ), это неопределенное поведение – все может случиться – это может сработать, оно может нанести вред ненароком, это может привести к некорректному повреждению и подобрать временную проблему.

Это настолько проблематично объяснить, что все может случиться с новичками. Они начинают «доказывать», что «это работает» (потому что он действительно работает на реализации C ++, который они используют) и спрашивает «что может быть неправильно с этим»? Какое краткое объяснение я могу дать, что побудило бы их просто не писать такой код?

«Поздравляем, вы определили поведение, которое компилятор имеет для этой операции. Я ожидаю, что отчет о поведении, который другие 200 компиляторов, которые существуют в мире, будут находиться у меня на столе до 10 утра завтра. теперь, ваше будущее выглядит многообещающим! ”

Undefined означает явно ненадежную. Программное обеспечение должно быть надежным. Вам не нужно больше говорить.

Замороженный пруд является хорошим примером неопределенной поверхности ходьбы. Просто потому, что вы делаете это через один раз, это не значит, что вы должны добавить ярлык к своему бумажному маршруту, особенно если вы планируете четыре сезона.

Мне приходят две возможности:

  1. Вы могли бы спросить их «только потому, что вы можете проехать по автостраде в противоположном направлении в полночь и выжить, вы бы делали это регулярно?»

  2. Более активное решение может состоять в том, чтобы настроить другую среду компилятора / запуска, чтобы показать им, как это происходит неэффективно при разных обстоятельствах.

Просто процитируйте со стандарта. Если они не могут принять это, они не программисты на С ++. Христиане отрицают Библию? 😉

1.9 Выполнение программы

  1. Семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину. […]

  2. Некоторые аспекты и операции абстрактной машины описаны в этом Международном стандарте в качестве реализации (например, sizeof(int) ). Они составляют параметры абстрактной машины. Каждая реализация должна включать документацию, описывающую ее характеристики и поведение в этих отношениях . […]

  3. Некоторые другие аспекты и операции абстрактной машины описаны в этом Международном стандарте как неопределенные (например, порядок оценки аргументов функции). Там, где это возможно, настоящий международный стандарт определяет набор допустимых видов поведения . Они определяют недетерминированные аспекты абстрактной машины. […]

  4. Некоторые другие операции описаны в этом Международном стандарте как неопределенные (например, эффект разыменования нулевого указателя). [Примечание: этот международный стандарт не налагает никаких требований на поведение программ, которые содержат неопределенное поведение . -End note]

Вы не можете понять, чем это.

Я бы объяснил, что если бы они не правильно написали код, их следующий обзор производительности не был бы счастливым. Это достаточная «мотивация» для большинства людей.

Пусть они пробуют свой путь, пока их код не выйдет из строя во время теста. Тогда слова не понадобятся.

Дело в том, что у новичков (у нас все было) есть некоторое количество эго и уверенности в себе. Все нормально. На самом деле, вы не могли быть программистом, если бы вы этого не сделали. Важно просвещать их, но не менее важно поддерживать их и не прервать свое начало в пути, подрывая их доверие к себе. Просто будьте вежливы, но докажите свою позицию фактами, а не словами. Будут только факты и доказательства.

Спокойно переопределите новые, новые [], удалите и удалите [] и посмотрите, сколько времени потребуется, чтобы он заметил;)

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

Мне нравится эта цитата:

Неопределенное поведение: оно может повредить ваши файлы, отформатировать ваш диск или отправить почту ненависти вашему боссу.

Я не знаю, кому это приписывать (возможно, это из Effective C ++ )?

Джон Вудс :

Короче говоря, вы не можете использовать sizeof () для структуры, элементы которой не определены, и если вы это сделаете, демоны могут вылететь из вашего носа.

«Демоны могут вылететь из вашего носа», просто должны быть частью словаря каждого программиста.

Говоря подробнее, поговорим о переносимости. Объясните, как часто программы необходимо портировать на разные ОС, не говоря уже о разных компиляторах. В реальном мире порты обычно выполняются людьми, отличными от оригинальных программистов. Некоторые из этих портов относятся даже к встроенным устройствам, где могут быть огромные затраты на обнаружение того, что компилятор решил иначе, чем ваше предположение.

Превратите человека в указатель. Скажите им, что они являются указателями на человека classа, и вы вызываете функцию «RemoveCoat». Когда они указывают на человека и говорят «RemoveCoat», все в порядке. Если у человека нет пальто, не беспокойтесь – мы проверяем это, все, что делает RemoveCoat, – это удалить верхний слой одежды (с проверкой на благочестие).

Теперь, что происходит, если они указывают где-то случайным, и говорят, что RemoveCoat – если они указывают на стену, тогда краска может отслаиваться, если они указывают на дерево, которое может выпасть из коры, собаки могут побриться, USS Enterprise может опустите его щиты в критический момент и т. д.!

Невозможно определить, что может случиться, поведение не было определено для этой ситуации – это называется неопределенным поведением, и его следует избегать.

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

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

Поскольку есть много вещей, о которых можно волноваться, ни один курс или книга никогда не заставят кого-то овладеть C ++ или, возможно, даже станут с ним хорошими.

Один из них был бы …

«Это» использование не является частью языка. Если мы скажем, что в этом случае компилятор должен сгенерировать код, который выйдет из строя, тогда это будет особенность, что-то вроде требования к производителю компилятора. Авторы стандарта не хотели давать ненужную работу над «функциями», которые не поддерживаются. В таких случаях они решили не принимать никаких поведенческих требований.

Просто покажите им Valgrind.

Скомпилируйте и запустите эту программу:

 #include  class A { public: A() { std::cout << "hi" << std::endl; } ~A() { std::cout << "bye" << std::endl; } }; int main() { A* a1 = new A[10]; delete a1; A* a2 = new A[10]; delete[] a2; } 

По крайней мере, при использовании GCC, это показывает, что деструктор получает только один элемент при выполнении одного удаления.

О единичном удалении на массивах POD. Направьте их на C ++ FAQ или попросите их запустить свой код через cppcheck .

Один момент, который еще не упоминается о неопределенном поведении, заключается в том, что если выполнение какой-либо операции приведет к неопределенному поведению, реализация, соответствующая стандартам, может быть законной, возможно, в попытке быть «полезной» или повысить эффективность, генерировать код, который потерпел бы неудачу, если такая операция были предприняты попытки. Например, можно представить многопроцессорную архитектуру, в которой может быть заблокирована любая ячейка памяти, и попытка получить доступ к заблокированному местоположению (за исключением того, чтобы разблокировать его) будет останавливаться до тех пор, пока указанное местоположение не будет разблокировано. Если блокировка и разблокировка были очень дешевыми (правдоподобно, если они реализованы на аппаратных средствах), такая архитектура может быть полезна в некоторых сценариях с несколькими streamами, поскольку реализация x++ как (атомарно считывание и блокировка x; добавление одного к считываемому значению; и написать x) будет гарантировать, что если два streamа одновременно выполняли x++ одновременно, результатом было бы добавить два к x. Предоставляемые программы записываются во избежание неопределенного поведения, такая архитектура может облегчить разработку надежного многопоточного кода, не требуя больших неуклюжих барьеров памяти. К сожалению, оператор вроде *x++ = *y++; может привести к тупиковой ситуации, если x и y являются ссылками на одно и то же место хранения, и компилятор попытался конвейеру кода как t1 = read-and-lock x; t2 = read-and-lock y; read t3=*t1; write *t2=t3; t1++; t2++; unlock-and-write x=t1; write-and-unlock y=t2; t1 = read-and-lock x; t2 = read-and-lock y; read t3=*t1; write *t2=t3; t1++; t2++; unlock-and-write x=t1; write-and-unlock y=t2; , Хотя компилятор мог избежать тупиковой ситуации, воздерживаясь от чередования различных операций, это может помешать эффективности.

Включите malloc_debug и delete массив объектов с деструкторами. free указателя внутри блока должно завершиться неудачно. Позвоните им всем и продемонстрируйте это.

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

Расскажите им о стандартах и ​​о том, как инструменты разработаны для соответствия стандартам. Все, что вне стандарта может работать или не работать, это UB.

Просто потому, что их программа, похоже, работает, является гарантией ничего; компилятор может сгенерировать код, который работает (как вы определяете «работу», когда правильное поведение не определено ?) в будние дни, но форматирует ваш диск по выходным. Читали ли они исходный код для своего компилятора? Изучите их разобранную продукцию?

Или напоминайте им только потому, что это происходит, чтобы «работать» сегодня не является гарантией его работы при обновлении версии вашего компилятора. Скажите им, чтобы они повеселились, обнаружив, что от этого исходят тонкие ошибки.

И действительно, почему бы и нет ? Они должны предоставлять обоснованный аргумент для использования неопределенного поведения, а не наоборот. Какая причина заключается в использовании delete вместо delete[] кроме лени? (Хорошо, есть std::auto_ptr . Но если вы используете std::auto_ptr с new[] -распределенным массивом, вы, вероятно, должны использовать std::vector .)

  • Когда вызов функции-члена в экземпляре null приводит к неопределенному поведению?
  • Изменился ли стандарт C ++ в отношении использования неопределенных значений и неопределенного поведения в C ++ 14?
  • Как спаривание нового с удалением может привести только к утечке памяти?
  • Виртуальный деструктор и неопределенное поведение
  • Можно ли рассматривать 2D-массив как непрерывный массив 1D?
  • Почему целочисленное переполнение на x86 с GCC вызывает бесконечный цикл?
  • Класс смешивания и структура
  • Арифметический сдвиг вправо дает фиктивный результат?
  • Почему это неопределенное поведение для удаления массива производных объектов с помощью базового указателя?
  • В C99, f () + g () не определено или просто неопределено?
  • Сопоставлено ли целочисленное переполнение по-прежнему неопределенным поведением в C ++?
  • Interesting Posts

    LINQ: использование INNER JOIN, Group и SUM

    Как восстановить систему из файлов WIM?

    Как интегрировать OpenCV Manager в Android-приложение

    Visual Studio 2013 не обнаруживает модульные тесты

    Гипер-V и поддержка виртуализации BIOS заставляют Windows 8 зависать при загрузке

    Наушники usb только воспроизводят определенный звук

    Передача аргументов интерактивной программе не интерактивно

    Как настроить Основной class в файле манифеста в банке, созданной проектом NetBeans

    Являются ли сценарии оболочки чувствительными к кодированию и окончаниям строк?

    Как изменить порт прослушивания RDP?

    Возможно ли восстановить vbuntu ubuntu, но сохранить данные в домашнем каталоге нетронутыми? (На Virtualbox или другом решении виртуальной машины)

    Как исправить ошибку регистрации Google Cloud Messaging: SERVICE_NOT_AVAILABLE?

    Вероятность получения дублирующегося значения при вызове метода GetHashCode () в строках

    Как выбрать MonoTouch и Objective-C?

    Быстрая двойная строка

    Давайте будем гением компьютера.