it-roy-ru.com

Определено ли поведение программы с неопределенным поведением на недоступном пути?

Рассматривать

void swap(int* a, int* b)
{
    if (a != b){
        *a = *a ^ *b;
        *b = *a ^ *b;
        *a = *a ^ *b;
    }   
}

int main()
{
    int a = 0;
    int b = 1;
    swap(&a, &b); // after this b is 0 and a is 1
    return a > b ? 0 : a / b;
}

swap - это попытка обмануть компилятор, чтобы он не оптимизировал программу.

Определено ли поведение этой программы? a / b никогда не будет достигнут, но если бы это было так, вы бы получили деление на ноль.

24
Bathsheba

Нет необходимости основывать позицию по этому вопросу на полезности любой данной конструкции кода или практики или на чем-либо, написанном о C++, будь то в его стандартном или в другом ответе SO, независимо от того, насколько похожи определения C++. быть. Ключевым моментом, который необходимо учитывать, является C's определение неопределенного поведения :

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

(C2011, 3.4.3/1; выделение добавлено)

Таким образом, неопределенное поведение запускается во времени («при ​​использовании» конструкции или данных), а не простым присутствием.* Удобно, что это согласуется с неопределенным поведением, возникающим из данных, и тем, что возникает из программных конструкций; стандарт не должен был быть последовательным там. И, как описывает другой ответ, это определение «при использовании» является хорошим выбором при проектировании, поскольку позволяет программам избегать выполнения неопределенного поведения, связанного с ошибочными данными.

С другой стороны, если программа делает выполняет неопределенное поведение, то из определения стандарта следует, что все поведение программы не определено. Эта последующая неопределенность является более общим видом, вытекающим из того факта, что UB, связанный непосредственно с ошибочными данными или конструкцией, может, в принципе, включать изменение поведения других частей программы, даже задним числом (или, по-видимому, так). Конечно, существуют ограничения на то, что может произойти, на других языках - так что нет, носовые демоны на самом деле не будут появляться, но они не обязательно настолько сильны, как можно предположить.


* Предостережение: некоторые программные конструкции являются used во время перевода. Они создают UB при переводе программы, в результате чего каждое выполнение программы имеет совершенно неопределенное поведение. Для несколько глупого примера: если исходный код вашей программы не заканчивается неэкранированным символом новой строки, то поведение программы полностью не определено (см. C2011, 5.1.1.2/1 , пункт 2).

26
John Bollinger

Поведение выражения, которое не оценивается, не имеет отношения к поведению программы. Поведение, которое было бы неопределенным, если выражение было оценено, не имеет отношения к поведению программы.

Если это так, то этот код будет бесполезен:

if (p != NULL)
    …; // Use pointer p.

(Ваши XOR могут иметь неопределенное поведение, поскольку они могут создавать представление ловушек. Вы можете победить оптимизацию для академических примеров, подобных этому, объявив объект энергозависимым. Если объект энергозависим, реализация C не может знать, может ли его значение измениться из-за к внешним средствам, поэтому каждое использование объекта требует реализации, чтобы прочитать его значение.)

22
Eric Postpischil

В общем случае код, который будет вызывать неопределенное поведение при выполнении, не должен иметь никакого эффекта, если он не выполняется. Однако есть несколько случаев, когда реальные реализации могут вести себя противоположным образом и отказываться генерировать код, который, хотя и не является нарушением ограничения, не может быть выполнен с определенным поведением.

extern struct foo z;

int main(int argc, char **argv)
{
    if (argc > 2) z;
    return 0;
}

В моем прочтении стандарта он явно характеризует преобразования lvalue для незавершенных типов как вызов Undefined Behavior (среди прочего, неясно, что реализация может генерировать код для такой вещи), поэтому стандарт не будет налагать никаких требований на поведение, если argc 3 или больше Я не могу определить какие-либо ограничения в Стандарте, которые приведенный выше код нарушил бы, однако, ни какое-либо поведение причины не должно быть полностью определено, если argc равен 2 или меньше. Тем не менее, многие компиляторы, включая gcc и clang, полностью отклоняют приведенный выше код.

0
supercat