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