Конечно, это четко определено.
Неважно, когда присваивается p=arr
. Вы не оцениваете p[0]
, вы подписываете результат (p=arr)
, то есть значение указателя, которое сохраняется в p
. Независимо от того, было ли оно сохранено, оно не меняет значение, и значение известно независимо от того, было ли p
изменено.
Аналогично, в *--p
нет неопределенного поведения. Поведение было бы неопределенным, если бы к одной и той же переменной обращались дважды, включая как минимум одну запись, между точками последовательности. Но p
доступен только один раз, как часть --p
. Он не читается снова (*p
), оператор разыменования применяется к результату --p
, который является четко определенным значением указателя.
Теперь это будет неопределенное поведение:
void* a;
void* p = &a;
reinterpret_cast<void**>(p = &p)[0] = 0;
как бы
int *pi = new int[5];
int i = **&++pi;
Должно быть ясно, что результатом преинкремента не является неупорядоченное чтение с записью, потому что утверждать, что есть гонка, значит утверждать, что ++p
никогда не может использоваться в качестве значения, и в этом случае оно должно автономно между точками последовательности, и вместо этого можно использовать постинкремент. Не было бы никакого преимущества в том, чтобы иметь в языке как пре-инкремент, так и постинкремент.