Как объяснить поведение undefined для новичков-новичков?
Существует несколько ситуаций, которые стандарт С++ присваивает как поведение undefined. Например, если я выделяю new[]
, тогда попробуйте освободить с помощью delete
(not delete[]
), что undefined поведение - может случиться что угодно - это может это может привести к сбою в сбоях, это может испортить что-то тихое и придумать временную проблему.
Это так проблематично объяснить, что все может случиться с новичками. Они начинают "доказывать", что "это работает" (потому что он действительно работает на реализации С++, который они используют) и спрашивает "что может быть неправильно с этим"? Какое краткое объяснение я могу дать, что побудило бы их просто не писать такой код?
Ответы
Ответ 1
"Поздравляем, вы определили поведение, которое компилятор имеет для этой операции. Ожидаю, что отчет о поведении, который другие 200 компиляторов, которые существуют в мире, будут находиться у меня на столе до 10 утра завтра." разочаруй меня сейчас, ваше будущее выглядит многообещающим!"
Ответ 2
Undefined означает явно ненадежную. Программное обеспечение должно быть надежным. Вам не нужно больше говорить.
Замороженный пруд является хорошим примером гусеничной поверхности undefined. Просто потому, что вы делаете это через один раз, это не значит, что вы должны добавить ярлык к вашему бумажному маршруту, особенно если вы планируете четыре сезона.
Ответ 3
Мне приходят в голову две возможности:
-
Вы могли бы спросить их "только потому, что вы можете проехать по автостраде в противоположном направлении в полночь и выжить, вы бы делали это регулярно?"
-
Более активное решение может состоять в том, чтобы настроить другую среду компилятора/запуска, чтобы показать им, как она не срабатывает эффектно при разных обстоятельствах.
Ответ 4
Проще говоря, из стандарта. Если они не могут принять это, они не программисты на C++. Христиане отрицают Библию?; -)
1.9 Выполнение программы
-
Семантические описания в этом международном стандарте определяют параметризованную недетерминированную абстрактную машину. [...]
-
Определенные аспекты и операции абстрактной машины описаны в этом Международном стандарте как с реализацией (например, sizeof(int)
). Они составляют параметры абстрактной машины. Каждая реализация должна включать документацию, описывающую ее характеристики и поведение в этих отношениях. [...]
-
Некоторые другие аспекты и операции абстрактной машины описаны в этом Международном стандарте как неуказанный (например, порядок оценки аргументов функции). Там, где это возможно, настоящий международный стандарт определяет набор допустимых видов поведения. Они определяют недетерминированные аспекты абстрактной машины. [...]
-
Некоторые другие операции описаны в этом международном стандарте как undefined (например, эффект разыменования нулевого указателя). [Примечание: этот международный стандарт не предъявляет требований к поведению программ, содержащих поведение undefined. -end note]
Вы не можете получить более четкое представление.
Ответ 5
Я бы сказал, что если они не будут правильно писать код, их следующий обзор производительности не будет счастливым. Это достаточная "мотивация" для большинства людей.
Ответ 6
Пусть они пробуют свой путь, пока их код не выйдет из строя во время теста. Тогда слова не понадобятся.
Дело в том, что у новичков (мы все были там) есть некоторое количество эго и уверенности в себе. Все в порядке. На самом деле, вы не могли быть программистом, если бы вы этого не сделали. Важно воспитывать их, но не менее важно поддерживать их и не прервать свое начало в пути, подрывая их доверие к себе. Просто будьте вежливы, но докажите свою позицию фактами, а не словами. Только факты и доказательства будут работать.
Ответ 7
Спокойно переопределите новый, новый [], удалите и удалите [] и посмотрите, как долго он его заметит;)
В противном случае... просто скажите ему, что он не прав, и укажите ему на С++. О да.. и в следующий раз будьте более осторожны при использовании людей, чтобы убедиться, что вы избегаете отверстий!
Ответ 8
Мне нравится эта цитата:
Undefined: он может испортить ваши файлы, отформатировать ваш диск или отправить почту ненависти
Ваш босс.
Я не знаю, кому это приписать (возможно, из Эффективный С++)?
Ответ 9
Джон Вудс:
Короче говоря, вы не можете использовать sizeof() для структуры, элементы которой не были и если вы это сделаете, демоны могут вылететь из вашего носа.
"Демоны могут вылететь из вашего носа" просто должны быть частью словаря каждого программиста.
Подробнее, поговорим о переносимости. Объясните, как часто программы необходимо портировать на разные ОС, не говоря уже о разных компиляторах. В реальном мире порты обычно выполняются людьми, отличными от оригинальных программистов. Некоторые из этих портов даже встроены в устройства, где могут быть огромные затраты на обнаружение того, что компилятор решил иначе, чем ваше предположение.
Ответ 10
Поверните человека в указатель. Скажите им, что они являются указателями на человека класса, и вы вызываете функцию "RemoveCoat". Когда они указывают на человека и говорят "RemoveCoat", все в порядке. Если у человека нет пальто, не беспокойтесь - мы проверяем это, все, что делает RemoveCoat, - это удалить верхний слой одежды (с проверкой на благочестие).
Теперь, что происходит, если они указывают где-то случайным, и говорят, что RemoveCoat - если они указывают на стену, тогда краска может отслаиваться, если они указывают на дерево, которое может разорвать кора, собаки могут брить себя, USS Enterprise может снизить свои щиты в критический момент и т.д.!
Нет способа определить, что может случиться, что поведение не было определено для этой ситуации - это называется поведением undefined, и его следует избегать.
Ответ 11
Скомпилируйте и запустите эту программу:
#include <iostream>
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. Направьте их на С++ FAQ или попросите их запустить свой код через cppcheck.
Ответ 12
С++ на самом деле не является языком для dilletantes, и просто перечислять некоторые правила и заставить их подчиняться без всякого сомнения будет делать для некоторых ужасных программистов; большинство самых глупых вещей, которые, как я вижу, люди говорят, вероятно, связаны с такими слепыми правилами следования/адвокатами.
С другой стороны, если они знают, что деструкторы не будут вызваны и, возможно, некоторые другие проблемы, тогда они позаботятся об этом. И что еще более важно, иметь возможность отлаживать его, если они когда-либо делаются это случайно, а также иметь возможность понять, насколько опасны многие функции С++.
Так как есть много вещей, о которых можно волноваться, ни один курс или книга никогда не заставят кого-то овладеть С++ или, возможно, даже станут с ним хорошими.
Ответ 13
Можно было бы...
"This" Использование не является частью языка. Если мы скажем, что в этом случае компилятор должен сгенерировать код, который выйдет из строя, то это будет особенность, что-то вроде требования к производителю компилятора. Авторы стандарта не хотели давать ненужную работу над "функциями", которые не поддерживаются. Они решили не принимать никаких поведенческих требований в таких случаях.
Ответ 14
Просто покажите им Valgrind.
Ответ 15
О том, что поведение undefined еще не упомянуто о том, что если выполнение какой-либо операции приведет к поведению undefined, реализация, соответствующая стандарту, может быть законно, возможно, в попытке быть "полезной" или повысить эффективность, сгенерировать код который потерпел бы неудачу, если бы такая операция была предпринята. Например, можно представить многопроцессорную архитектуру, в которой может быть заблокирована любая ячейка памяти, и попытка получить доступ к заблокированному местоположению (за исключением того, чтобы разблокировать его) будет останавливаться до тех пор, пока указанное местоположение не будет разблокировано. Если блокировка и разблокировка были очень дешевыми (правдоподобными, если они реализованы на аппаратных средствах), такая архитектура может быть полезна в некоторых сценариях с несколькими потоками, поскольку реализация x++
as (атомарно считывается и блокируется x; добавляет значение для чтения; атомно разблокировать и написать x) будет гарантировать, что если два потока одновременно выполняли x++
одновременно, результатом было бы добавить два в x. Предоставляемые программы записываются во избежание поведения undefined, такая архитектура может облегчить разработку надежного многопоточного кода, не требуя больших неудобных барьеров памяти. К сожалению, такой оператор, как *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;
. Хотя компилятор мог избежать тупиковой ситуации, воздерживаясь от чередования различных операций, это может помешать эффективности.
Ответ 16
Включите malloc_debug и delete
массив объектов с деструкторами. free
ввод указателя внутри блока должен завершиться неудачно. Вызовите их всех вместе и продемонстрируйте это.
Вам нужно будет подумать о других примерах, чтобы создать свой авторитет, пока они не поймут, что они новички, и там многое знать о С++.
Ответ 17
Расскажите им о стандартах и о том, как инструменты разработаны для соответствия стандартам. Все, что вне стандарта может работать или не работать, это UB.
Ответ 18
Просто потому, что их программа, похоже, работает, является гарантией ничего; компилятор может сгенерировать код, который работает (как вы даже определяете "работу", когда правильное поведение undefined?) в будние дни, но форматирует ваш диск по выходным. Читали ли они исходный код для своего компилятора? Изучите их разобранный выход?
Или напоминайте им только потому, что это происходит, чтобы "работать" сегодня не является гарантией того, что он работает при обновлении версии вашего компилятора. Скажите им, чтобы они повеселились, обнаружив, что из-за этого появляются тонкие ошибки.
И действительно, почему бы и нет? Они должны предоставлять оправданный аргумент для использования поведения undefined, а не наоборот. Какая причина использовать delete
вместо delete[]
, кроме лени? (Хорошо, там std::auto_ptr
. Но если вы используете std::auto_ptr
с массивом new[]
-allocated, вы, вероятно, должны использовать std::vector
.)