Как объяснить неопределенное поведение новичкам всезнайки? - PullRequest
34 голосов
/ 10 февраля 2010

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

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

Ответы [ 18 ]

2 голосов
/ 10 февраля 2010

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

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

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

1 голос
/ 20 ноября 2011

Одна вещь, еще не упомянутая в отношении неопределенного поведения, заключается в том, что, если выполнение какой-либо операции приведет к неопределенному поведению, реализация, соответствующая стандартам, может законно, возможно, попытаться быть «полезной» или повысить эффективность, сгенерировать код, который потерпит неудачу, если такая операция была предпринята. Например, можно вообразить многопроцессорную архитектуру, в которой любое местоположение памяти может быть заблокировано, и попытка получить доступ к заблокированному местоположению (кроме как разблокировать его) будет останавливаться до тех пор, пока рассматриваемое местоположение не будет разблокировано. Если блокировка и разблокировка были очень дешевыми (правдоподобно, если они реализованы в аппаратном обеспечении), такая архитектура могла бы быть полезной в некоторых многопоточных сценариях, поскольку реализация x++ as (атомарное чтение и блокировка x; добавление единицы к значению чтения; атомарная разблокировка и запись x) гарантируют, что если два потока одновременно выполнят 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;. Хотя компилятор может избежать тупиковой ситуации, воздерживаясь от чередования различных операций, это может снизить эффективность.

1 голос
/ 10 февраля 2010

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

#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. Направьте их на C ++ FAQ или попросите их выполнить код через cppcheck .

1 голос
/ 10 февраля 2010

Один будет ...

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

1 голос
/ 10 февраля 2010

Просто покажи им Вальгринд.

0 голосов
/ 10 февраля 2010

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

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

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

0 голосов
/ 10 февраля 2010

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

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

0 голосов
/ 10 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...